You are on page 1of 273

1

Developing for the J2EE Tomcat Platform

Table of Contents
1.
2.
3.
4.
5.
6.
7.
8.

Java Server Pages


JavaBeans
Tag Libraries
Introduction to Struts
Advanced Struts
Enterprise JavaBeans
A Case Study
Enterprise Objects Framework

Chapter 1. Introduction to J2EE


Servlets and Java Server Pages

What is J2EE?
Java Server Pages
Java Servlets: A bit of history
JSP Syntax
An Example with JBoss and Tomcat
MyGreeting: A simple web application
Exercises

After completing this chapter, the student will be able to


explain Servlets and Java Server Pages (JSPs) and their roles in
developing web applications.
have JBoss deploy with the Tomcat container in an integrated virtual
machine.

Goals

create JSPs for use in systems producing dynamically generated


HTML.

Prerequisites

The student will need to be famliar with web browsers, an understanding of


HTML, and familiarity with Java and its use in programming.

Objectives

This chapter is presented to provide an understanding of the role that Servlets


and Java Server Pages play in the web application development process and
to understand what J2EE is.

What is J2EE?
J2EE (Java 2 Enterprise Edition) is a specification for developing enterprise and
distributed applications from JavaSoft (Sun Microsystems). J2EE is not one thing; it
encompasses a large set of technologies:

JavaServer Pages (JSP)


Servlets
Enterprise JavaBeans (EJB)
JDBC
Java Naming and Directory Interface (JNDI)

support for XML


Java Messaging
Java Transaction Support
JavaMail
Java support for CORBA

In this course, we will talk about JSP, servlets, EJB, JDBC, and touch on JNDI.
What can using these technologies accomplish?
J2EE is useful for creating web applications like Yahoo! Mail, a web-based stock trading
system such as E*TRADE, or an online auction house like eBay. Anytime there is a need
for many people to access a collection of data in a distributed manner, an implementation
of the J2EE specification can provide a solution.

Where is J2EE?
Since J2EE is a specification, it isn't located anywhere. Instead vendors make their
products adhere to the J2EE specification.
Examples of the vendors offering J2EE compliant products are

Netscape/AOL iPlanet
BEA WebLogic
IBM WebSphere
The JBoss Group's JBoss

In this course, we will concentrate on JBoss, which is a free, open-source J2EE server.
The JBoss server itself is written in Java, so it can run on Windows, Linux, Mac OS X,
Solaris, or any other platform which fully supports the Java virtual machine.

Chapter 2. Java Server Pages

4
Java Server Pages (JSP) are Sun's solution to the generation of dynamic HTML. With
JSPs, you can generate HTML on the fly, which is important because
the content of the HTML may depend upon user submitted data
the information represented by the HTML is dynamic by nature
the HTML may be customizable on a per user basis

JSPs are HTML-like pages that can contain a mixture of HTML tags, data, and Java code.
Here is an example of a JSP that contains a simple for loop to count to 10:
code/Chapter1/counter/counter.jsp
<%@ page language="java" %>
<HTML>
<BODY>
<%
for (int counter = 1; counter <= 10; counter++) {
%>
<%= counter %> Mississippi,<BR>
<%
}
%>
<B>Ready or not, here I come!</B>
</BODY>
</HTML>

We then deploy this on a web server that knows how to handle JSPs. (We'll see how to
deploy it later in this chapter.) The JSP will be mapped to a specific web address on that
server, and when a user comes along and types in that web address, the web server will
find this JSP, dynamically compile the Java code in it, run it, merge it with the HTML
tags that are interspersed around the Java code, and output an HTML page back to the
user's browser. The generated HTML will look like this:

<HTML>
<BODY>
1 Mississippi,<BR>
2 Mississippi,<BR>
3 Mississippi,<BR>
4 Mississippi,<BR>

5 Mississippi,<BR>
6 Mississippi,<BR>
7 Mississippi,<BR>
8 Mississippi,<BR>
9 Mississippi,<BR>
10 Mississippi,<BR>
<B>Ready or not, here I come!</B>
</BODY>
</HTML>

Java Servlets: A bit of history


Servlets are pure Java objects that generate HTML by writing it to a stream. The files do
not contain any HTML tags; they simply contain Java code which write out HTML (as
strings) to produce the page that is sent to the user's browser. Historically, this used to be
the only way to generate dynamic content in Java, but it is quite painful to develop any
large scale application with a bunch of servlets running around doing println()
statements. This is why Sun invented JSPs in the first place: to compete with the ease-ofdevelopment of Microsoft's ASP files, which could -- you guessed it -- mix HTML tags
and code in a single file.
JSPs and servlets are two different ways to accomplish the same goal: generating
dynamic HTML pages using Java code. One puts Java code in your HTML, and one puts
HTML in your Java code. Functionally, they are equivalent. In fact, under the covers, the
web server takes a JSP and converts it to the corresponding servlet and dynamically
compiles it.
Here is a Java servlet that produces the same content as the JSP we just saw above:
code/Chapter1/counter/counterAsServlet.java
import javax.servlet.*;
import java.io.*;
import java.util.*;
public class SimpleServlet extends GenericServlet {
public void service(ServletRequest request,
ServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");

code/Chapter1/counter/counterAsServlet.java
printWriter pw = response.getWriter();
pw.println("<HTML>");
pw.println("<BODY>");
for(int counter = 1; counter <= 10; counter++) {
pw.println(counter + " Mississippi, <BR>");
}
pw.println("<B>Ready or not, here I come!</B>");
pw.println("</BODY>");
pw.println("</HTML>");
pw.close();
}

You can still write Java servlets manually if you want to, and many people still swear by
them. However, JSPs are more flexible, and as we'll see in later chapters, can be
combined with other features to create a powerful templating engine. We will not be
writing servlets in this course, although we will see them sneak in behind the scenes in
chapter 4.

JSP Syntax
Directives
JSPs live in an object called a container, which is essentially a server. JSPs can define
information for the container with directives.
Here is what directives look like in a general form:
<%@ directive attribute="someValue" attribute="anotherValue" ... %>

There are three directives:

<%@ page ... %> specifies information that affects the page
<%@ include ... %> includes a file at the location of the include

directive

(parsed)

<%@ taglib ... %>

allows the use of custom tags in the page

<%@ page language="java" %>

will always be the first line of every JSP file.

Declarations
Declarations are used to specify supplemental methods and variables. You can think of
these are the page's private functions; they can only be called by the JSP where they are
defined, or by another JSP that includes it (using the <@ include > directive).

7
Here is a sample declaration:
<%!
// this integer can be used anywhere in this JSP page
private int myVariable = -1;
// this function can be called from anywhere in this JSP page
public boolean isPositive() {
return ( myVariable > 0 );
}
%>

Scriptlets
Scriptlets are bits of Java code. They can do anything (the full power of Java is available
in every JSP), but they will most likely concentrate on generating HTML code or setting
up variables to be part of later expressions (see below).
The first JSP we saw above, counter.jsp, contained a scriptlet with a for loop.
Expressions
Expressions are special-purpose mini-scriptlets used for evaluating expressions. This
could be something as simple as outputting the value of a variable, or a more complicated
Java expression, like calling a function and outputting the result.
The JSP example counter.jsp also contained an expression:
<%= counter %>

Note that counter is defined as an int, but we do not need to explicitly convert it to a
string. Expressions are implicitly converted to strings when they are output. (The
println() function works the same way; it can take pretty much anything and print it.
That makes sense if you think about it. Remember that JSPs automatically get converted
to Java servlets under the covers. So an expression like this in a JSP file is converted to a
println statement in a servlet that you never see. So anything println can print, an
expression can contain, without any explicit conversion.)

An Example with JBoss and Tomcat


Now lets create a web application using JBoss with Tomcat. JBoss is an open-sourced
J2EE server from the JBoss Group (www.jboss.org). It provides support for integrating
Tomcat, which is the open-sourced Servlet and JSP engine from the Apache Group's
Jakarta project (jakarta.apache.org).

8
Note: If JBoss with Tomcat is not already installed, the instructor will provide assistance
with getting it installed.
The files for a J2EE web application are placed in a web application archive file (.war
extension). Inside the web application archive are JSPs, HTML pages, .properties files,
XML configuration files, images, and class files, as well as any customized data files that
might need to be included. At a minimum, the web application archive will consist of a
WEB-INF folder at the top level. Inside the WEB-INF folder will be a web.xml file. The
web.xml file provides the J2EE server with information specific to the web application
contained in the same web application archive. For example, it provides the name for the
default page to serve, if one is not specified.

MyGreeting: A simple web application


Do this:
1. Create a directory named MyGreeting
2. Within the MyGreeting directory, create a subdirectory named WEB-INF
(capitalization matters, so be precise, and that's a dash, not an underscore)
code/Chapter1/MyGreeting
MyGreeting
|
+-- HelloWorld.jsp (*)
|
+-- WEB-INF
|
+-- web.xml (*)
(*) denotes a file

3. Create a JSP file named MyGreeting/HelloWorld.jsp


4. Add the following text to the HelloWorld.jsp file:
Code/Chapter1/MyGreeting/HelloWorld.jsp
<%@ page language="java" %>
<HTML>
<BODY>
<%
for (int counter = 1; counter <= 10; counter++) {
%>

Code/Chapter1/MyGreeting/HelloWorld.jsp
<%

Hello World!<BR>

}
%>
</BODY>
</HTML>

5. Create the web.xml file in MyGreeting/WEB-INF/web.xml


6. Add the following text to web.xml:
code/Chapter1/MyGreeting/WEB-INF/web.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
"http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
<web-app>
</web-app>

7. Drop to a command line and change to MyGreeting directory


8. Create the web application archive using the command-line jar utility:
c:\j2ee\code\Chapter1\MyGreeting> jar cvf MyGreeting.war *.jsp WEB-INF

9. Copy MyGreeting.war to the deploy folder in your JBoss installation:


c:\j2ee\code\Chapter1\MyGreeting> copy MyGreeting.war
c:\j2ee\jboss\deploy\

10. Start JBoss by running the run.bat script provided in the bin folder of the JBoss
installation. Because JBoss relies on relative paths to set up its CLASSPATH, this
is a two-step process. First, change to the jboss/bin/ directory, then execute
run.bat. Also, be sure to specify the tomcat argument on the command line so
that it also starts the Tomcat servlet engine.

10

c:\j2ee\code\Chapter1\MyGreeting> cd c:\j2ee\jboss\bin\
c:\j2ee\jboss\bin> run.bat tomcat

11. Open a web browser and go to


http://localhost:8080/MyGreeting/HelloWorld.jsp

Exercises
Exercise 1. Current time
Create a web application that displays the current time in a web page. Provide a link
inside the web page to reload the page with the an updated time. Use JSPs in order to
produce the dynamic content.

11

JavaBeans

What is Scope?
Every JSP has 5 implicit objects
The request-response cycle
A JSP using the request and response objects
Three parts of a web application
A JSP using the pageContext, session, and application implicit objects
Using Java Classes with JSP
FirstBean: a simple JavaBean
Accessor and mutator methods
SecondBean: a JavaBean with accessor and mutator methods
What is Persistence?
SerializableBean: a JavaBean with Persistence
Exercises

Goals

After completing this chapter, the student will be able to


explain scope.
discuss implicit objects.

make a JavaBean.

In order to complete this chapter successfully, the student must have


a basic understanding of the request-response cycle.
an understanding of JSP directives (<%@), scriptlets (<%), expressions
Prerequisites
(<%=), and declarations (<%!).

Objectives

a basic understanding of how to construct a JSP.

The purpose of this chapter is to provide the student with the necessary
information about implicit objects and JavaBeans.

What is Scope?
Scope is the idea that an object belongs to a certain part of an application. The five main
scopes of a web application are request, response, page, session, and application. This
means that an object may be a part of a request, a response, an individual page, a session,
or an instance of the application. For example, if we said, "the employee object has
session scope," we would mean that the employee object only existed within a certain
session.

Every JSP has 9 implicit objects

12
This section discusses the implicit objects that represent the request, response, page,
session, and application scopes. In JSPs, implicit objects are created by the JSP
environment, so the developer does not need to initialize an implicit object. Implicit
objects can only be used in JSP scriptlets and expressions.

Implicit object Description


request

The ServletRequest or HttpServletRequest being serviced

response

The Servlet Response or HttpServletResponse that will receive the


generated HTML output

pageContext

The PageContext object for this page. This object is a central respiratory
for attribute data for the page, request , session and application

session

If the Jsp Page uses an HttpSession, it is availabele here under the name
session

application

The Servlet context Object

Out

The character output stream used to generate the output HTML

Config

The Servletconfig object for this servlet context

Page

A Reference to the jsp page itself.

Exception

An uncaught exception that causes the error page to be invoked.this


variable is available only to pages with isErrorpage=true

The request-response cycle


A very basic, generic request-response cycle consists of two parts. The request is where
the client asks for data from the server, and response is where the server sends data to the
client.

Capabilities of the request and response objects


In JSPs, the request-response cycle is represented mainly by the request and response
objects. The request object handles the information sent from the client and the
response object handles the information sent to the client.

13

Methods involved in the request


return type

method name

description

HttpSession getSession()

returns the session associated with


the request

String

returns the value associated with the


header name of the request

getHeader(String headerName)

Enumeration getHeaderNames()

returns all the header names


associated with a request

Cookie[]

getCookies()

returns the cookies associated with a


request

Object

getAttribute(String
attributeName)

returns the object that is paired with


an attribute's name

setAttribute(String
nameOfTheAttribute, Object
valueOfTheAttribute)

sets an attribute named

void

nameOfTheAttribute to
valueOfTheAttribute

the value of

Methods involved in the response


return
type

method name

description

void

addCookie(Cookie cookie)

adds the specified cookie to the


response

void

addHeader(String headerName, String


value)

adds the header to the response

void

sendError(int statusCode) throws


IOException

sends a predefined error message


back to the client

void

sendRedirect(String newURL) throws


IOException

redirects the client browser to a


different URL

A JSP using the request and response objects

code/Chapter2/RequestResponse/requestresponse.jsp
<%@ page language="java" %>
<HTML>
<BODY>

14

code/Chapter2/RequestResponse/requestresponse.jsp
character encoding = <%= response.getCharacterEncoding() %>
<br/><br/>
Headers
<br/><br/>
<% java.util.Enumeration headerNames = request.getHeaderNames();
while(headerNames.hasMoreElements()) {
String headerName = (String)headerNames.nextElement();
String header = (String)request.getHeader(headerName);
%>
<%= headerName %> = <%=header%>
<br/>
<% } %>
</BODY>
</HTML>

Three parts of a web application


Information in a web application can be stored in the application scope, the session scope,
and the page scope. The page scope refers to all information that only pertains to a
specific instance of a given page. The server keeps the page-specific information as long
as the page exists. The session scope contains information pertaining to a session
instance. The application scope contains information that is available to all sessions in the
application, as long as the application is running.

The pageContext, session and application objects


A programmer can access information stored in the page, the session, and the application
scope, using the pageContext, session, and application objects, respectively.

Basic pageContext methods

15
return type

method name

description

Object

findAttribute(String
attributeName)

searches the page, session,


application and request scopes for
an attribute named attributeName - it
returns the attribute or null, if the
attribute does not exist

Object

getAttribute(String
attributeName)

returns the object that is paired with an


attribute's name
sets an attribute named

void

setAttribute(String
nameOfTheAttribute,
Object
valueOfTheAttribute)

HttpServletRequest

getRequest()

HttpServletResponse getResponse()

nameOfTheAttribute to
valueOfTheAttribute

the value of

returns the request object associated


with the page
returns the response object associated
with the page

Basic session methods


return
type

method name

description

Object

getAttribute(String
attributeName)

returns the object that is paired with an


attribute named attributeName

setAttribute(String
nameOfTheAttribute, Object
valueOfTheAttribute)

sets an attribute named

void

String[] getAttributeNames()

nameOfTheAttribute to
valueOfTheAttribute

the value of

returns an array of the names of the


attributes for a given session

Basic application methods


return type

method name

description

Object

getAttribute(String
attributeName)

returns the object that is paired with


an attribute's name

setAttribute(String
nameOfTheAttribute, Object
valueOfTheAttribute)

sets an attribute named

void

Enumeration getAttributeNames()

nameOfTheAttribute to
valueOfTheAttribute

the value of

returns an array of the names of the


attributes for a given application

A JSP using the pageContext, session, and application implicit objects

16
Here is a JSP that uses the pageContext, session, and application implicit objects.
code/Chapter2/ImplicitObjects/implicitobjects.jsp
<%@ page language="java" %>
<%
if (pageContext.getAttribute("pageCount")==null) {
pageContext.setAttribute("pageCount", new Integer(0));
}
if (session.getAttribute("sessionCount")==null) {
session.setAttribute("sessionCount",new Integer(0));
}
if (application.getAttribute("appCount")==null) {
application.setAttribute("appCount",new Integer(0));
}
%>
<HTML>
<BODY>
<% Integer count = (Integer)pageContext.getAttribute("pageCount");
pageContext.setAttribute("pageCount", new
Integer(count.intValue()+1));
Integer count2 = (Integer)session.getAttribute("sessionCount");
session.setAttribute("sessionCount",new
Integer(count2.intValue()+1));
Integer count3 = (Integer)application.getAttribute("appCount");
application.setAttribute("appCount",new
Integer(count3.intValue()+1));
%>
Page Count = <%= pageContext.getAttribute("pageCount")%>
<br/>Session count = <%= session.getAttribute("sessionCount")%>
<br/>Application count = <%= application.getAttribute("appCount")%>
<br/>Time = <%= new java.sql.Time(System.currentTimeMillis()) %>
</BODY>
</HTML>

Using Java Classes with JSP


Not all the code for a JSP needs to be written in the JSP itself. In fact, in good coding
practice no code should be found in the JSP. This provides greater separation between the
clients' view of the application and the logic of the application. Separation between logic
and presentation means a more easily modifiable application.
creates an instance of a specified class that is usable in a JSP. The instance
of a Java class in a JSP is commonly referred to as a bean or a JavaBean. In order to
create an instance, named greeter, of a custom class named Greetings with a package
named thePackage, a JSP must state the following:
jsp:useBean

17
<jsp:useBean id="greeter" class="thePackage.Greetings"
scope="session" />

The scope attribute specifies whether the instantiated object has a page, session, or
application scope. The JSP can then make calls to the object's methods in scriptlets and/or
declarations:

<%
greeter.setNameOfPersonToBeGreeted("Jill");
greeter.setLanguage("Spanish");
%>
<%=
<%=

greeter.sayHello() %>
greeter.sayGoodBye() %>

Naming Confusion: Two Types of JavaBeans


It is important to distinguish between a JavaBean used in a GUI development tool and a
JavaBean used in a server-side application. This chapter discusses the later type of
JavaBean. A JavaBean, or sometimes just called a bean, is basically an instance of a Java
class. The two main characteristics of a server-side JavaBean are that accessor and
mutator methods should be provided for all properties in the Java class and that the
JavaBean may be persistent.
JavaBeans are normally deployed as part of a package. For this chapter, we will put our
JavaBeans in the com.masslight.beans package.
When deploying a JavaBean as part of a web application, the package structure is
recreated as a nested directory structure within the WEB-INF/classes/ directory. So our
com.masslight.beans package will become the directory path WEBINF/classes/com/masslight/beans/, and our Java classes will go inside that
directory.

FirstBean: a simple JavaBean


Do this:
1. Create the following directory structure. Create a FirstBean directory to hold all
of the files for this web application, then within that, create a WEB-INF directory,
then within that create a classes directory, then com, then masslight, then
beans. Whew!

18

code/Chapter2/FirstBean
FirstBean
|
+-- beanExample.jsp (*)
|
+-- WEB-INF
|
+-- web.xml (*)
|
+-- classes
|
+-- com
|
+-- masslight
|
+-- beans
|
+-- GreeterBean.java (*)
(*) denotes a file

2. Create the JSP in the FirstBean directory:


code/Chapter2/FirstBean/beanExample.jsp
<%@ page language="java" %>
<html>
<head><title>Hello Example</title></head>
<body>
<jsp:useBean id="greeter" class="com.masslight.beans.GreeterBean"
scope="session"/>
<%= greeter.sayHello() %>
</body>
</html>

3. Create the web.xml file in the WEB-INF directory:


code/Chapter2/FirstBean/WEB-INF/web.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
"http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
<web-app>
</web-app>

19

4. Create the Java file in the FirstBean/WEB-INF/classes/com/masslight/beans


code/Chapter2/FirstBean/WEB-INF/classes/com/masslight/beans/GreeterBean.java
package com.masslight.beans;
public class GreeterBean {
public String sayHello() {
return("Hello, World!");
}
}

5. Change to the beans directory and compile the Java file with javac, Sun's
command-line Java compiler:
c:\j2ee\code\Chapter2\FirstBean\WEB-INF\classes\com\masslight\beans>
javac *.java

6. Change back to the FirstBean directory and create the .war file, using the jar
utility like you did in the previous chapter
c:\j2ee\code\Chapter2\FirstBean> jar cvf FirstBean.war *.jsp WEB-INF

7. Deploy the web application by copying the .war file to the JBoss deployment
directory
c:\j2ee\code\Chapter2\FirstBean> copy FirstBean.war
c:\j2ee\jboss\deploy\

8. Test the application by going to


http://localhost:8080/FirstBean/beanExample.jsp

Accessor and mutator methods


JavaBeans require properties to have accessor and mutator methods. A property is another
name for a class-wide variable. Accessors provide a standardized way of accessing the
value of a property. Mutators provide a standard way to modify the value of a property. In
fact, it is good coding practice to provide and use accessor and mutator methods for
properties in all Java classes. To make the accessor and mutator methods for a property,
the standard practice is to prepend the name of the property with the word 'get' for the
get method name and 'set' for the set method name. In the accessor and mutator methods'
names, the property name is capitalized, though the property's name should be declared in
the lower case. An example of the accessor and mutator methods for a simple variable of

20
type variable_type and name exampleVariable would be:

public void setExampleVariable(variable_type value)


public variable_type getExampleVariable()

If the variable_type is boolean, the set method would be the same and the get method
would be:

public boolean isExampleVariable()

If the property is an array, then the getter and setter methods are:

public
public
public
public
value)

variable_type[] getExampleVariable()
variable_type getExampleVariable(int location_in_array)
void setExampleVariable(variable_type[])
void setExampleVariable(int location_in_array, variable_type

SecondBean: a JavaBean with accessor and mutator methods


Do this:
1. Create the directory structure. Create a SecondBean directory to hold all the files
for this web application. Within SecondBean, the directory structure is the same
as it was for FirstBean.
code/Chapter2/SecondBean
SecondBean
|
+-- beanExample.jsp (*)
|
+-- WEB-INF
|
+-- web.xml (*)
|
+-- classes
|
+-- com
|
+-- masslight
|
+-- beans

21

code/Chapter2/SecondBean
|
+-- NumberBean.java (*)
(*) denotes a file

2. Create the JSP in the SecondBean directory:


code/Chapter2/SecondBean/beanExample.jsp
<%@ page language="java" %>
<jsp:useBean id="number" class="com.masslight.beans.NumberBean"
scope="session"/>
<html>
<head><title>Second Bean Example</title></head>
<body>
JavaBean state starts at <%= number.getIntegerProperty() %>
<% number.setIntegerProperty(2); %>
<br> JavaBean state is now <%= number.getIntegerProperty() %>
</body>
</html>

3. Create the web.xml file in WEB-INF


code/Chapter2/SecondBean/WEB-INF/web.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
"http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
<web-app>
</web-app>

4. Create the Java file in WEB-INF/classes/com/masslight/beans/


code/Chapter2/SecondBean/WEBINF/classes/com/masslight/beans/NumberBean.java
package com.masslight.beans;
public class NumberBean {

22

code/Chapter2/SecondBean/WEBINF/classes/com/masslight/beans/NumberBean.java
private int integerProperty = 1;
public int getIntegerProperty() {
return integerProperty;
}
public void setIntegerProperty(int newInteger) {
integerProperty = newInteger;
}
}

5. Compile the Java classes, in the same way you did with FirstBean
6. Create the .war file, in the same way you did with FirstBean. Call it
SecondBean.war

7. Deploy the web application by copying SecondBean.war to the JBoss


deployment directory
8. Test the application at
http://localhost:8080/SecondBean/beanExample.jsp

What is Persistence?
The properties of a bean can be saved and then retrieved at a later time - in other words, a
bean can be persistent. In order to be persistent, a JavaBean needs to implement the
java.io.Serializable interface. The java.io.Serializable interface does not have
any methods to implement. It is used to indicate that the class that implements it can be
saved and retrieved. To learn how to save and retrieve a bean, please look at the
following example.

SerializableBean: a JavaBean with Persistence


Do this:
1. Create the directory structure. Create a SerializableBean directory; within that,
the structure is the same as FirstBean and SecondBean.
code/Chapter2/SerializableBean
SerializableBean
|

23

code/Chapter2/SerializableBean
+-- firstPage.jsp (*)
|
+-- secondPage.jsp (*)
|
+-- WEB-INF
|
+-- web.xml (*)
|
+-- classes
|
+-- com
|
+-- masslight
|
+-- beans
|
+-- SerializableBean.java (*)
(*) denotes a file

2. Create 2 JSPs in the SerialiableBean directory:


code/Chapter2/SerializableBean/firstPage.jsp
<%@ page language="java" %>
<jsp:useBean id="theBean" class="com.masslight.beans.SerializableBean"
scope="page" />
<html>
<title>Serialized bean, Page One</title>
<body>
<br>
<%= theBean.getIntegerProperty() %>
<% theBean.setIntegerProperty(8+theBean.getIntegerProperty());
%>

<br>
<%= theBean.getIntegerProperty() %>
<%
theBean.save(theBean, "theBeanFile.ser");
%>
<br>
<a
href="http://localhost:8080/SerializableBean/secondPage.jsp">click
here</a>
</body>
</html>

24

code/Chapter2/SerializableBean/secondPage.jsp
<%@ page language="java" %>
<jsp:useBean id="saver" class="com.masslight.beans.SerializableBean"
scope="session"/>
<html>
<title>Serialized bean, Page Two</title>
<body>
<%
com.masslight.beans.SerializableBean anotherBean =
(com.masslight.beans.SerializableBean)saver.retrieve("theBeanFile.ser");
%>
<%= anotherBean.getIntegerProperty() %>
<br>
<% anotherBean.setIntegerProperty(8+
anotherBean.getIntegerProperty()); %>
<%= anotherBean.getIntegerProperty() %>
</body>
</html>

3. Create the web.xml file in WEB-INF


code/Chapter2/SerializableBean/WEB-INF/web.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
"http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
<web-app>
</web-app>

4. Create the Java file in WEB-INF/classes/com/masslight/beans/


code/Chapter2/SerializableBean/WEBINF/classes/com/masslight/beans/SerializableBean.java
package com.masslight.beans;
import java.beans.*;
import java.io.Serializable;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.FileInputStream;

25

code/Chapter2/SerializableBean/WEBINF/classes/com/masslight/beans/SerializableBean.java
import java.io.ObjectInputStream;
public class SerializableBean implements Serializable {
private int integerProperty=8;
public int getIntegerProperty() {
return integerProperty;
}
public void setIntegerProperty(int newInteger) {
integerProperty= newInteger;
}
public boolean save(Object object, String file_name) {
boolean status = true;
try{
FileOutputStream fos;
ObjectOutputStream oos;
fos = new FileOutputStream(file_name);
oos = new ObjectOutputStream(fos);
oos.writeObject(object);
oos.close();
}
catch (Exception e) {
status = false;
System.out.println("Error with saving: " + e.toString());
}
return status;
}
public Object retrieve(String file_name) {
Object object = new Object();
try {
FileInputStream fis;
ObjectInputStream ois;
fis = new FileInputStream(file_name);
ois = new ObjectInputStream(fis);
object = ois.readObject();
ois.close();
}
catch (Exception e) {
System.out.println("Error with retrieval: " + e.toString());
}
return object;
}}

5.
6.
7.
8.

Compile the Java file.


Create SerializableBean.war
Deploy
Test at http://localhost:8080/SerializableBean/firstPage.jsp

26

Exercises
Exercise 1. Application vs. session
Make a JSP that displays the last time it was ever hit by anyone, and the last time it has
been hit by you personally. If it has never been hit by anyone before, it should display
"This application has never been used before." If this is your first time, it should display
"This is your first time using this application." (Hint: use the application and session
objects.
Exercise 2. Calculator
Make a JavaBean that does simple arithmetic. The application should consist of a JSP
page and a JavaBean. The JavaBean should provide methods, that given two numbers,
produce the sum, the difference, the product, and the quotient. The JavaBean should then
produce output for the JSP similar to the following screen shot. The calls to the JavaBean
methods from the JSP should look like this:

<%=
<%=
<%=
<%=

calculator.add(1, 1) %><br>
calculator.subtract(4, 2) %><br>
calculator.multiply(4, 5) %><br>
calculator.divide(30, 6) %>

27

Chapter 3.
Tag Libraries

What Are Tags?


Using Tags in JSP
SimpleTag: a simple tag-based web application
Tag Attributes
AttributeTag: using attributes in tag libraries
Using Scope Objects in Tags
Exercises

Goals

After completing this chapter, the student will be able to


make a simple custom tag.

make a complex custom tag.

In order to complete this chapter successfully, the student must understand


JSP directives.
how to construct a JSP.
Prerequisites

Objectives

how to construct and call JavaBeans.

The purpose of this chapter is to provide the student with the necessary
information about tags and tag libraries.

What Are Tags?


Tags provide another way for a JSP developer to separate the presentation of an
application from the logic of the application. The implementation of a tag differs from the
implementation of a JavaBean. A tag's class must implement methods unique to tag
classes in order for the tag to be executed properly in the JSP. Also, the tags in an
application require a tag library descriptor XML file that contains information about each
of the tags. A tag class extends javax.servlet.jsp.tagext.TagSupport or
javax.servlet.jsp.tagext.BodyTagSupport.

Using Tags in JSP


In a Java Server Page, a tag is initialized with a declaration that looks like this:
<%@ taglib uri="WEB-INF/lib/tag_library_name.tld" prefix="prefix_name"
%>

28
The tag_library_name.tld, used in the declaration of the tag, is the name of the tag
library that has the information about the tag called tag_name. In JBoss, the
tag_library_name.tld must be located in the /WEB-INF/lib pathway in order to be
located by the JBoss engine. To use a very simple tag in the JSP:
<prefix_name:tag_name/>

A tag can contain a body:


<prefix_name:tag_name>
this is called the body of the tag
</prefix_name:tag_name>

A tag can also have attributes:


<prefix_name:tag_name attributeOne="hey" />

Creating a simple tag class

the class should extend javax.servlet.jsp.tagext.TagSupport


should override the method called public int doStartTag() extends
JspTagException {

can override the method called public int doEndTag() extends


JspTagException {

usually does not need to override public void release()

SKIP_BODY versus EVAL_BODY_INCLUDE and SKIP_PAGE versus EVAL_PAGE

is returned at the end of doStartTag to tell the JSP to not output the content
contained in the body of the tag. The EVAL_BODY_INCLUDE is returned at the end of the
doStartTag if the content in the body of the tag should be outputted. The SKIP_PAGE is
returned from the doEndTag to tell the JSP to skip the rest of the content of the JSP, while
the EVAL_PAGE signifies that the JSP should evaluate the rest of the contents of the page.
The default values returned are SKIP_BODY and EVAL_PAGE.
SKIP_BODY

doStartTag

Called when the open tag is read in the JSP - returns either SKIP_BODY
or EVAL_BODY_INCLUDE

doEndTag

Called when the close tag is called in the JSP - returns either SKIP_PAGE
or EVAL_PAGE

release

Called when the JSP is finished with the tag - resets the state of the tag

The tag library descriptor file

29
The tag library descriptor file helps a JSP map a tag to a Java class. It is an XML file with
a .tld suffix.
Elements used in the .tld file to describe tags:
taglib

signifies that a tag library will be described - required

tlibversion

the tag library version - required

jspversion

the version of JSP that will be used

shortname

the name of the tag library - required

uri

the Uniform Resource Identifier for the tag library

info

the comment tag for the developer

tag

signifies that a tag located in the taglib will be described - see


following table for elements of a tag

Elements used in the tag tag:


name

the name of the tag - required

tagclass

the path information to the tag's class - required

teiclass

the path information of the subclass of TagExtraInfo - this is not


discussed in this chapter

bodycontent

specifies the content type of the tag - can be empty, JSP, or


tagdependent - empty means that the tag has no body, JSP means
that there is a body that can interpreted as JSP, and tagdependent
means that the tag will interpret the body as non-JSP content

info

for comments

attribute

signifies that an attribute of the tag will be described - see


following table

Elements used in the attribute tag


name

the name of the attribute that the tag might need in the declaration
- required

required

boolean value to specify if the attribute is required - can be either


true or false

rtexprvalue

specifies if the attribute can take a scriptlet expression as a value,


which allows the attribute's value to determined when the attribute
is set - if this element is not specified, the value is false - can be
either true or false

30

SimpleTag: a simple tag-based web application


Do this:
1. Create the directory structure. Create a SimpleTag directory, then create the usual
WEB-INF and classes, then create the directory structure that represents the
package structure, which in this example is
com.masslight.tagExampleClasses.
code/Chapter3/SimpleTag
SimpleTag
|
+-- tagExample.jsp (*)
|
+-- build.xml (*)
|
+-- WEB-INF
|
+-- web.xml (*)
|
+-- tagExampleLib.tld (*)
|
+-- classes
|
+-- com
|
+-- masslight
|
+-- tagExampleClasses
|
+-- HelloWorldTag.java (*)
(*) denotes a file

2. Create the JSP in the SimpleTag directory


code/Chapter3/SimpleTag/tagExample.jsp
<%@ page language="java" %>
<!-- declare the tag -->
<%@ taglib uri="WEB-INF/tagExampleLib.tld" prefix="greeter" %>
<html>
<body>
<!-- use the tag -->
<greeter:Hello/>
</body>
</html>

31

3. Create the web.xml file in WEB-INF


code/Chapter3/SimpleTag/WEB-INF/web.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
"http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
<web-app>
<taglib>
<taglib-uri>/WEB-INF/tagExampleLib.tld</taglib-uri>
<taglib-location>/WEB-INF/tagExampleLib.tld</taglib-location>
</taglib>
</web-app>

4. Create the Java file in WEB-INF/classes/com/masslight/tagExampleClasses/


code/Chapter3/SimpleTag/WEBINF/classes/com/masslight/tagExampleClasses/HelloWorldTag.java
package com.masslight.tagExampleClasses;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
import java.io.*;
public class HelloWorldTag extends javax.servlet.jsp.tagext.TagSupport {
public int doStartTag() throws JspException {
try {
pageContext.getOut().print("Hello, World!");
} catch (Exception ex) {
throw new JspException("IO problems");
}
return SKIP_BODY;
}
}

5. Create the tag library descriptor file in WEB-INF. This maps each tag used in
tagExample.jsp to a specific Java class, which in this example is
com.masslight.tagExampleClasses.HelloWorldTag.
code/Chapter3/SimpleTag/WEB-INF/tagExampleLib.tld
<?xml version="1.0" encoding="ISO-8859-1" ?>

32

code/Chapter3/SimpleTag/WEB-INF/tagExampleLib.tld
<!DOCTYPE taglib
PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN"
"http://java.sun.com/j2ee/dtds/web-jsptaglib_1_1.dtd">
<taglib>
<tlibversion>1.0</tlibversion>
<jspversion>1.1</jspversion>
<shortname>utility</shortname>
<info>
A simple tag library for the examples
</info>
<tag>
<name>Hello</name>
<tagclass>com.masslight.tagExampleClasses.HelloWorldTag</tagclass>
<bodycontent>empty</bodycontent>
<info>
Print Hello World
</info>
</tag>
</taglib>

6. Finally, add the build.xml file, which we will use to automate building and
deploying the web application. build.xml goes in the root SimpleTag directory,
next to the JSPs.
code/Chapter3/SimpleTag/build.xml
<project name="SimpleTag" default="dist" basedir=".">
<!-- set global properties for this build -->
<property environment="env"/>
<property name="top" value="."/>
<property name="src" value="."/>
<property name="build" value="build"/>
<property name="dist" value="dist"/>
<property name="war_dir" value="${dist}/lib"/>
<property name="war_file" value="${war_dir}/SimpleTag.war"/>
<property name="webinf" value="${top}/WEB-INF"/>
<property name="web.xml" value="${webinf}/web.xml"/>
<property name="classes" value="${webinf}/classes"/>
<property name="lib" value="${top}/WEB-INF/lib"/>
<property name="servlet.jar" value="$
{env.TOMCAT_HOME}/lib/servlet.jar"/>
<property name="deploy" value="${env.JBOSS_HOME}/deploy"/>
<target name="clean">
<!-- Delete our the ${build} and ${dist} directory trees -->

33

code/Chapter3/SimpleTag/build.xml
<delete dir="${build}"/>
<delete dir="${dist}"/>
<delete dir="${war_dir}"/>
</target>
<target name="init">
<!-- Create the build directory structure used by compile and dist
-->
<mkdir dir="${build}"/>
<mkdir dir="${dist}"/>
<mkdir dir="${war_dir}"/>
</target>
<target name="compile" depends="init">
<!-- Compile the java code from ${src} into ${build} -->
<javac
srcdir="${top}/${src}"
destdir="${build}"
classpath="${servlet.jar}"/>
</target>
<target name="dist" depends="compile">
<!-- Put everything in a war file -->
<war warfile="${war_file}" webxml="${web.xml}">
<!-- include all JSPs in root level -->
<fileset dir="${top}/${src}">
<include name="*.jsp"/>
</fileset>
<!-- include all tag libraries in WEB-INF,
but not web.xml (that's handled separately) -->
<webinf dir="${webinf}">
<include name="*.tld"/>
</webinf>
<!-- include all compiled classes -->
<classes dir="${build}"/>
</war>
</target>
<target name="deploy">
<!-- Copy the war file to the JBoss deploy directory -->
<copy file="${war_file}" todir="${deploy}"/>
</target>
<target name="all" depends="clean,dist,deploy"/>
</project>

7. Do not manually compile the Java files. Instead, use Ant to compile. Start at the
root SimpleTag directory and call Ant with the compile argument. You should
see output like this:

34

c:\j2ee\code\Chapter3\SimpleTag> ant compile


Buildfile: build.xml
init:
[mkdir] Created dir: C:\j2ee\code\Chapter3\SimpleTag\build
[mkdir] Created dir: C:\j2ee\code\Chapter3\SimpleTag\dist
[mkdir] Created dir: C:\j2ee\code\Chapter3\SimpleTag\dist\lib
compile:
[javac] Compiling 1 source file to
C:\j2ee\code\Chapter3\SimpleTag\build
BUILD SUCCESSFUL
Total time: 2 seconds

8. Do not manually create the .war distribution file. Instead, use Ant to create it.
c:\j2ee\code\Chapter3\SimpleTag> ant dist
Buildfile: build.xml
init:
dist:

[war] Building war:


C:\j2ee\code\Chapter3\SimpleTag\dist\lib\SimpleTag.war
BUILD SUCCESSFUL
Total time: 2 seconds

9. Do not manually copy the .war file into your JBoss deploy directory. Instead, use
Ant to deploy it.
c:\j2ee\code\Chapter3\SimpleTag> ant deploy
Buildfile: build.xml
deploy:
[copy] Copying 1 file to C:\j2ee\jboss\deploy
BUILD SUCCESSFUL
Total time: 0 seconds

10. Test the application at http://localhost:8080/SimpleTag/tagExample.jsp


Why Ant rocks
It doesn't look like Ant is much better than doing things manually, but's only because we
intentionally did each step we could see what it looked like. In fact, Ant can easily
combine steps, by specifying in the build.xml file that a particular step requires other
steps to be performed in succession:

35
<target name="all" depends="clean,dist,deploy"/>

And you execute it like this:


c:\j2ee\code\Chapter3\SimpleTag> ant all
Buildfile: build.xml
clean:
[delete] Deleting directory C:\j2ee\code\Chapter3\SimpleTag\build
[delete] Deleting directory C:\j2ee\code\Chapter3\SimpleTag\dist
init:
[mkdir] Created dir: C:\j2ee\code\Chapter3\SimpleTag\build
[mkdir] Created dir: C:\j2ee\code\Chapter3\SimpleTag\dist
[mkdir] Created dir: C:\j2ee\code\Chapter3\SimpleTag\dist\lib
compile:
[javac] Compiling 1 source file to
C:\j2ee\code\Chapter3\SimpleTag\build
dist:
[war] Building war:
C:\j2ee\code\Chapter3\SimpleTag\dist\lib\SimpleTag.war
deploy:
[copy] Copying 1 file to C:\j2ee\jboss\deploy
all:
BUILD SUCCESSFUL
Total time: 2 seconds

Voila! A completely clean compile and deployment in one easy step!

Output of SimpleTag application

36

Tag Attributes
Attributes must be declared in the tag library descriptor, set as a class wide variable in the
tag class with an accessor that uses the same name as declared in the tag library
descriptor, and if the attribute is required, given a value when the tag is initialized in the
JSP. If the tag in the tag library descriptor looked like this:
<tag>
<name>exampleTagName</name>
<tagclass>com.masslight.tags.ExampleTag</tagclass>
<bodycontent>empty</bodycontent>
<info>This is an example tag to demonstrate how attributes work</info>
<attribute>
<name>attributeOne</name>
<required>true</required>
</attribute>
<attribute>
<name>attributeTwo</name>
<required>false</required>
</attribute>
</tag>

then the class would need to include the following code:


public class ExampleTag extends TagSupport {
int attributeOne = 0;
String attributeTwo = 0;
....
....
public void setAttributeOne(String tempAttributeOne) {
attributeOne = Integer.valueOf(tempAttributeOne).intValue();
}
public void setAttributeTwo(String tempAttributeTwo) {
attributeTwo = tempAttributeTwo;
}
....
....

37
and the JSP might look something like this - remember that attributeTwo was not
required:
<%@ taglib uri="WEB-INF/lib/tagLibraryName.tld" prefix="example" %>
<example:exampleTagName attributeOne="1"/>

AttributeTag: using attributes in tag libraries


Do this:
1. Create the directory structure. Create an AttributeTag directory, then WEB-INF,
classes, and the directory structure com/masslight/tagExampleClasses.
code/Chapter3/AttributeTag
AttributeTag
|
+-- tagExample.jsp (*)
|
+-- build.xml (*)
|
+-- WEB-INF
|
+-- web.xml (*)
|
+-- tagExampleLib.tld (*)
|
+-- classes
|
+-- com
|
+-- masslight
|
+-- tagExampleClasses
|
+-- GreetingAttributeTag.java (*)
(*) denotes a file

2. Create the JSP in the AttributeTag directory.


code/Chapter3/AttributeTag/tagExample.jsp
<%@ page language="java" %>
<%@ taglib uri="WEB-INF/tagExampleLib.tld" prefix="greeter" %>

38

code/Chapter3/AttributeTag/tagExample.jsp
<html>
<body>
<greeter:Greeting greeting="Hey" />
</body>
</html>

3. Create the web.xml file in WEB-INF


code/Chapter3/AttributeTag/WEB-INF/web.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
"http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
<web-app>
<taglib>
<taglib-uri>
</taglib-uri>
<taglib-location>
</taglib-location>
</taglib>
</web-app>

4. Create the Java file in WEB-INF/classes/com/masslight/tagExampleClasses/


code/Chapter3/AttributeTag/WEBINF/classes/com/masslight/tagExampleClasses/GreetingAttributeTag.java
package com.masslight.tagExampleClasses;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
import java.io.*;
public class GreetingAttributeTag extends
javax.servlet.jsp.tagext.TagSupport {
public String greeting = "Hello";
public int doStartTag() throws JspTagException {
try {
pageContext.getOut().print(greeting);
} catch (Exception ex) {
throw new JspTagException("IO problems");
}
return SKIP_BODY;

39

code/Chapter3/AttributeTag/WEBINF/classes/com/masslight/tagExampleClasses/GreetingAttributeTag.java
}
public void setGreeting(String newGreeting) {
greeting = newGreeting;
}
}

5. Create the tag library descriptor file in WEB-INF


code/Chapter3/AttributeTag/WEB-INF/tagExampleLib.tld
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE taglib
PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN"
"http://java.sun.com/j2ee/dtds/web-jsptaglib_1_1.dtd">
<taglib>
<tlibversion>1.0</tlibversion>
<jspversion>1.1</jspversion>
<shortname>utility</shortname>
<info>
A simple tag library for the examples
</info>
<tag>
<name>Greeting</name>
<tagclass>com.masslight.tagExampleClasses.GreetingAttributeTag</tagclass>
<info>Print a greeting specified by the greeting attribute</info>
<bodycontent>empty</bodycontent>
<attribute>
<name>greeting</name>
<required>true</required>
</attribute>
</tag>
</taglib>

6. Create the build.xml file in the root AttributeTag directory.


code/Chapter3/AttributeTag/build.xml
<project name="AttributeTag" default="dist" basedir=".">
<!-- set global properties for this build -->
<property environment="env"/>
<property name="top" value="."/>
<property name="src" value="."/>

40

code/Chapter3/AttributeTag/build.xml
<property
<property
<property
<property

name="build" value="build"/>
name="dist" value="dist"/>
name="war_dir" value="${dist}/lib"/>
name="war_file" value="${war_dir}/AttributeTag.war"/>

<property name="webinf" value="${top}/WEB-INF"/>


<property name="web.xml" value="${webinf}/web.xml"/>
<property name="classes" value="${webinf}/classes"/>
<property name="lib" value="${top}/WEB-INF/lib"/>
<property name="servlet.jar" value="$
{env.TOMCAT_HOME}/lib/servlet.jar"/>
<property name="deploy" value="${env.JBOSS_HOME}/deploy"/>
<target name="clean">
<!-- Delete our the ${build} and ${dist} directory trees -->
<delete dir="${build}"/>
<delete dir="${dist}"/>
<delete dir="${war_dir}"/>
</target>
<target name="init">
<!-- Create the build directory structure used by compile and dist
-->
<mkdir dir="${build}"/>
<mkdir dir="${dist}"/>
<mkdir dir="${war_dir}"/>
</target>
<target name="compile" depends="init">
<!-- Compile the java code from ${src} into ${build} -->
<javac
srcdir="${top}/${src}"
destdir="${build}"
classpath="${servlet.jar}"/>
</target>
<target name="dist" depends="compile">
<!-- Put everything in a war file -->
<war warfile="${war_file}" webxml="${web.xml}">
<!-- include all JSPs in root level -->
<fileset dir="${top}/${src}">
<include name="*.jsp"/>
</fileset>
<!-- include all tag libraries in WEB-INF,
but not web.xml (that's handled separately) -->
<webinf dir="${webinf}">
<include name="*.tld"/>
<exclude name="web.xml"/>
</webinf>
<!-- include all compiled classes -->
<classes dir="${build}"/>

41

code/Chapter3/AttributeTag/build.xml
</war>
</target>
<target name="deploy">
<!-- Copy the war file to the JBoss deploy directory -->
<copy file="${war_file}" todir="${deploy}"/>
</target>
<target name="all" depends="clean,dist,deploy"/>
</project>

7. ant all to compile, jar, and deploy. You should see output like this:
C:\work\j2ee\code\Chapter3\AttributeTag> ant all
Buildfile: build.xml
clean:
init:
[mkdir] Created dir: C:\work\j2ee\code\Chapter3\AttributeTag\build
[mkdir] Created dir: C:\work\j2ee\code\Chapter3\AttributeTag\dist
[mkdir] Created dir:
C:\work\j2ee\code\Chapter3\AttributeTag\dist\lib
compile:
[javac] Compiling 1 source file to
C:\work\j2ee\code\Chapter3\AttributeTag\build
dist:

[war] Building war:


C:\work\j2ee\code\Chapter3\AttributeTag\dist\lib\AttributeTag.war
deploy:
[copy] Copying 1 file to C:\j2ee\jboss\deploy
all:
BUILD SUCCESSFUL
Total time: 8 seconds

8. Go to http://localhost:8080/AttributeTag/tagExample.jsp to test your


application

Using Scope Objects in Tags

42
It is important to note that pageContext, session, and application objects can be
accessed from a tag class. This means that attributes from any and all of the different
scopes can be used in a tag class.
Creating a complex tag class

the class should extend javax.servlet.jsp.tagext.BodyTagSupport


should override the method called public int doStartTag() throws
JspTagException {

can override the method public void doInitBody() throws

JspTagException {
needs the method public int doAfterBody() throws JspTagException {
should override the method called public int doEndTag() throws
JspTagException {
does not need to override the public void release() method
the bodycontent tag in the tag descriptor library should be JSP

The tag class that extends TagBody allows the developer to execute code before and after
the body of the tag. Instead of returning SKIP_BODY or EVAL_BODY_INCLUDE, the
doStartTag method returns SKIP_BODY or EVAL_BODY_TAG. The doAfterBody method
returns either the SKIP_BODY or the EVAL_BODY_TAG which allows the tag to loop through
the tag's body until some condition is met. The content of the tag's body can be accessed
by a call to getBodyContent() which returns a BodyContent object. The developer
needs to write the contents of the body to the output stream by making a call to the
writeOut() method of the BodyContent object. To write out the content of the body, one
must write out contents of the getPreviousOut() method. If the developer does not
make this call, then nothing from the body of the tag will be shown. The doInitBody
method returns void and is not such an important method as the doAfterBody method.
doInitBody
Called when the JSP enters the body of the tag - returns nothing
doAfterBody

Called when the JSP leaves the body of the tag - returns SKIP_BODY or
EVAL_BODY_TAG

Do this:
1. Create the directory structure. The root directory is named ComplexTag; the rest is
the same as the previous examples.
2. Create tagExample.jsp, RepeatHelloTag.java, web.xml,
tagExampleLib.tld, and build.xml
code/Chapter3/ComplexTag
ComplexTag
|
+-- tagExample.jsp (*)
|

43

code/Chapter3/ComplexTag
+-- build.xml (*)
|
+-- WEB-INF
|
+-- web.xml (*)
|
+-- tagExampleLib.tld (*)
|
+-- classes
|
+-- com
|
+-- masslight
|
+-- tagExampleClasses
|
+-- RepeatHelloTag.java (*)
(*) denotes a file

code/Chapter3/ComplexTag/tagExample.jsp
<%@ page language="java" %>
<%@ taglib uri="WEB-INF/tagExampleLib.tld" prefix="greeter" %>
<html>
<body>
<greeter:RepeatHello>
Hello Again<br>
</greeter:RepeatHello>
</body>
</html>

code/Chapter3/ComplexTag/WEBINF/classes/com/masslight/tagExampleClasses/RepeatHelloTag.java
package com.masslight.tagExampleClasses;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
import java.io.*;
public class RepeatHelloTag extends
javax.servlet.jsp.tagext.BodyTagSupport {
int count = 1;
int numberOfIterations = 3;
String greeting = new String("Hello <BR>");
public int doStartTag() throws JspTagException {

44

code/Chapter3/ComplexTag/WEBINF/classes/com/masslight/tagExampleClasses/RepeatHelloTag.java
try {
pageContext.getOut().print(greeting);
} catch (Exception ex) {
throw new JspTagException("IO problems");
}
return EVAL_BODY_TAG;

}
public int doAfterBody() throws JspTagException {
try {
if (count < numberOfIterations) {
count++;
return EVAL_BODY_TAG;
}
else {
BodyContent bodyContent = getBodyContent();
bodyContent.writeOut(getPreviousOut());
return SKIP_BODY;
}
}
catch (Exception e) {
throw (new JspTagException("error"));
}
}

code/Chapter3/ComplexTag/WEB-INF/web.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
"http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
<web-app>
<taglib>
<taglib-uri>/WEB-INF/tagExampleLib.tld</taglib-uri>
<taglib-location>/WEB-INF/tagExampleLib.tld</taglib-location>
</taglib>
</web-app>

code/Chapter3/ComplexTag/WEB-INF/tagExampleLib.tld
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE taglib
PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN"
"http://java.sun.com/j2ee/dtds/web-jsptaglib_1_1.dtd">

45

code/Chapter3/ComplexTag/WEB-INF/tagExampleLib.tld
<taglib>
<tlibversion>1.0</tlibversion>
<jspversion>1.1</jspversion>
<shortname>utility</shortname>
<info>
A simple tag library for the examples
</info>
<tag>
<name>RepeatHello</name>
<tagclass>com.masslight.tagExampleClasses.RepeatHelloTag</tagclass>
<bodycontent>JSP</bodycontent>
<info>Print Hello Multiple Times</info>
</tag>
</taglib>

code/Chapter3/ComplexTag/build.xml
<project name="ComplexTag" default="dist" basedir=".">
<!-- set global properties for this build -->
<property environment="env"/>
<property name="top" value="."/>
<property name="src" value="."/>
<property name="build" value="build"/>
<property name="dist" value="dist"/>
<property name="war_dir" value="${dist}/lib"/>
<property name="war_file" value="${war_dir}/ComplexTag.war"/>
<property name="webinf" value="${top}/WEB-INF"/>
<property name="web.xml" value="${webinf}/web.xml"/>
<property name="classes" value="${webinf}/classes"/>
<property name="lib" value="${top}/WEB-INF/lib"/>
<property name="servlet.jar" value="$
{env.TOMCAT_HOME}/lib/servlet.jar"/>
<property name="deploy" value="${env.JBOSS_HOME}/deploy"/>
<target name="clean">
<!-- Delete our the ${build} and ${dist} directory trees -->
<delete dir="${build}"/>
<delete dir="${dist}"/>
<delete dir="${war_dir}"/>
</target>
<target name="init">
<!-- Create the build directory structure used by compile and dist
-->
<mkdir dir="${build}"/>
<mkdir dir="${dist}"/>

46

code/Chapter3/ComplexTag/build.xml
<mkdir dir="${war_dir}"/>
</target>
<target name="compile" depends="init">
<!-- Compile the java code from ${src} into ${build} -->
<javac
srcdir="${top}/${src}"
destdir="${build}"
classpath="${servlet.jar}"/>
</target>
<target name="dist" depends="compile">
<!-- Put everything in a war file -->
<war warfile="${war_file}" webxml="${web.xml}">
<!-- include all JSPs in root level -->
<fileset dir="${top}/${src}">
<include name="*.jsp"/>
</fileset>
<!-- include all tag libraries in WEB-INF,
but not web.xml (that's handled separately) -->
<webinf dir="${webinf}">
<include name="*.tld"/>
<exclude name="web.xml"/>
</webinf>
<!-- include all compiled classes -->
<classes dir="${build}"/>
</war>
</target>
<target name="deploy">
<!-- Copy the war file to the JBoss deploy directory -->
<copy file="${war_file}" todir="${deploy}"/>
</target>
<target name="all" depends="clean,dist,deploy"/>
</project>

3. ant all to compile, jar, and deploy.


4. Go to http://localhost:8080/ComplexTag/tagExample.jsp to test your
application

Exercises

47
Exercise 1. Fibonacci
Make an application that calculates the Fibonacci sequence with a tag that uses a
repeating body. The application should produce the first n elements of the arithmetic
sequence, using a tag that takes an attribute specifying the value of n. The actual element
should be calculated using a JavaBean. The JavaBean method should be in the body of
the tag. The tag would look something like this:
<arithmetic_sum:first_sequence numberOfElements = "7">
<%= bean.calculateElement() %>
</arithmetic_sum:first_sequence>

48

Chapter 4. Struts Framework


Forms Processing

The Model-View-Controller Architecture


What is Struts?
Struts Tags
Creating Beans
Other Bean Tags
Bean Output
Creating HTML Forms
The ActionForm class
The Action class
SimpleStruts: a simple Struts application
Exercises

Goals

After completing this chapter, the student will be able to


understand the MVC architecture.
set up an application using Struts.
use the Struts bean and html tags.

process user input from HTML forms through Struts.

Prerequisites

The student will need to have an understanding of JSP, JavaBeans, and


custom tags.

Objectives

This chapter is presented to provide the student with an understanding of the


Struts framework.

The Model-View-Controller Architecture


"Model-View-Controller" is a way to build applications that promotes complete
separation between business logic and presentation. It is not specific to web applications,
or Java, or J2EE (it predates all of these by many years), but it can be applied to building
J2EE web applications.
The "view" is the user interface, the screens that the end user of the application actually
sees and interacts with. In a J2EE web application, views are JSP files. For collecting
user input, you will have a JSP that generates an HTML page that contains one or more
HTML forms. For displaying output (like a report), you will have a JSP generates an
HTML page that probably contains one or more HTML tables. Each of these is a view: a
way for the end user to interact with the system, putting data in, and getting data out.
When the user clicks 'Submit' in an HTML form, the request (complete with all the form
information) is sent to a "controller". In a J2EE web application, controllers are
JavaBeans. The controller's job is to take the data entered by the user (in the HTML form

49
that the JSP generated) and pass it to the "model", which is a separate Java class that
contains actual business logic. The model does whatever it does (for instance, store the
user's data in a database), then returns some result back to the controller (perhaps a new
ID value from the database, or perhaps just a result code saying "OK, I'm done"). The
controller takes this value and figures out what the user needs to see next, and presents
the user with a new view (for instance, a new JSP file that displays a confirmation that
the data they entered was successfully saved).
This all sounds like a lot of work, and it is. But there is a point to architecting
applications this way: flexibility. The beauty of model-view-controller separation is that
new views and controllers can be created independently of the model. The model -again, this is pure business logic -- knows nothing of HTML forms or JSP pages. The
model defines a set of business functions that only ever get called by controllers, and the
controllers act as proxies between the end user (interacting with the view) and the
business logic (encapsulated in the model). This means that you can add a new view and
its associated controller, and your model doesn't know or care that there are now two
different ways for human beings to interact with the application.
For instance, in an application with complicated data entry screens, you could add a JSP
that generated a quick-edit form with default values instead of a longer, standard form.
Both JSPs (the short form and the long form) could use the same controller; default
values could simply be stored in the HTML <form> as hidden input values, and the
controller would never know the difference. Or you could create a completely new
interface -- a desktop application in addition to a web application. The desktop
application would have views implemented in some interface-building tool, and have
associated controllers for each screen. But these controllers would call the business
functions in the model, just like the controllers in the web application. This is called
"code reuse", specifically "business logic reuse", and it's not just a myth: it really can
happen, and the Model-View-Controller architecture is one way to make it happen.

What is Struts?
Struts is a framework that promotes the use of the Model-View-Controller architecture
for designing large scale applications. The framework includes a set of custom tag
libaries and their associated Java classes, along with various utility classes. The most
powerful aspect of the Struts framework is its support for creating and processing webbased forms. We will see how this works later in this chapter.

Struts Tags
Common Attributes

50
Almost all tags provided by the Struts framework use the following attributes:

Attribute Used for


id

the name of a bean for temporary use by the tag

name

the name of a pre-existing bean for use with the tag

property

the property of the bean named in the name attribute for use with the tag

scope

the scope to search for the bean named in the name attribute

Referencing Properties
Bean properties can be referenced in three different ways: simple, nested, or indexed.
Shown here are examples for referencing the properties each way:

Reference
Method

Example

simple

<!-- uses tutorial.getAnAttribute() -->


<bean:write name="tutorial" property="anAttribute"/>

nested

<!-- uses tutorial.getAnAttribute().getAnotherAttribute()


-->
<bean:write name="tutorial"
property="anAttribute.anotherAttribute"/>

indexed

<!-- uses tutorial.getSomeAttributes(3) to access the -->


<!-- fourth element of the someAttributes property array
-->
<bean:write name="tutorial" property="someAttributes[3]"/>

flavorful mix of
methods

<!-- uses
foo.getSomeAttributes(2).getSomeMoreAttributes(1) -->
<bean:write name="foo"
property="goo[2].someAttributes[1]"/>

Creating Beans
Beans are created by Java code or tags.
Here is an example of bean creation with Java code:
// Creating a Plumber bean in the request scope
Plumber aPlumber = new Plumber();
request.setAttribute("plumber", aPlumber);

Beans can be created with the <jsp:useBean></jsp:useBean> tag:

51
<!-- If we want to do <jsp:setProperty ...></jsp:setProperty> or -->
<!-- <jsp:getProperty ... ></jsp:getProperty> -->
<!-- we first need to do a <jsp:useBean ... ></jsp:useBean> -->
<jsp:useBean id="aBean" scope="session" class="java.lang.String">
creating/using a bean in session scope of type java.lang.String
</jsp:useBean>

Most useful is the creation of beans with Struts tags:


<!-- Constant string bean -->
<bean:define id="greenBean" value="Here is a new constant string bean;
pun intended."/>
<!-- Copying an already existent bean, frijole, to a new bean, lima -->
<bean:define id="lima" name="frijole"/>
<!-- Copying an already existent bean, while specifying the class -->
<bean:define id="lima" name="frijole"
class="com.SomePackageName.Beans.LimaBean"/>
<!-- Copying a bean property to a different scope -->
<bean:define id="goo" name="foo" property="geeWhiz" scope="request"
toScope="application"/>

Other Bean Tags


The Struts framework provides other tags for dealing with issues concerning copying
cookies, request headers, JSP implicity defined objects, request parameters, web
application resources, Struts configuration objects, and including the dynamic response
data from an action. These tags are not discussed here, but it is important to be aware of
their existence.

<bean:cookie ... >


<bean:header ... >
<bean:page ... >
<bean:parameter ... >
<bean:header ... >
<bean:resource ... >
<bean:struts ... >

Bean Output

52
The <bean:message> and <bean:write> tags from the Struts framework will write bean
and aplication resources properties into the current HttpResponse object.

This tag allows locale specific messages to be displayed by looking up


the message in the application resources .properties file.

<bean:message
... >

<!-- looks up the error.divisionByZero resource -->


<!-- and writes it to the HttpResponse object -->
<bean:message key="error.divisionByZero"/>
<!-- looks up the prompt.name resource -->
<!-- and writes it to the HttpResponse object; -->
<!-- failing that, it writes the string -->
<!-- contained in the attribute arg0-->
<bean:message key="prompt.name" arg0='Enter a name:'/>

This tag writes the string equivalent of the specified bean or bean
property to the current HttpResponse object.
<bean:write ..
<!-- writes the value of
. >
customer.getStreetAddress().toString() -->
<!-- to the HttpResponse object -->
<bean:write name="customer" property="streetAddress"/>

Creating HTML Forms


Quite often information needs to be collected from a user and processed. Without the
ability to collect user input, a web application would be useless. In order to get the users
information, an html form is used. User input can come from several widgets, such as text
fields, text boxes, check boxes, pop-up menus, and radio buttons. The data corresponding
to the user input is stored in an ActionForm class. A configuration file called strutsconfig.xml is used to define exactly how the user input are processed.
The following diagram roughly depicts the use of Struts for using forms.

53

The Struts html tags are used to generate the widgets in the html that will be used in
gathering the users data. There are also tags to create a form element, html body
elements, links, images, and other common html elements as well as displaying errors.
Below are the tags provided by html section of the Struts framework and a short
description of each.
Note: Most of the tags for use with forms are able to take the name and property
attributes representing the initial state of the widgets and for capturing the state that the
widgets were in when a form submission occurred.

<html:base>

<html:button>

Generates a <base> tag. This tag should be used inside of a


<head> tag.
Generates an <input type="button"> tag. This tag should be
used inside a <form> element.
Cancel

<html:cancel>

<html:checkbox>
<html:multibox>

Generates an <input type="submit"> tag and causes the


Action servlet not to invoke its validate() method. This tag
should be used inside a <form> element.
Wheat

Wood

Stone

Sheep

Clay

54

<html:checkbox>

Generates an <input type="checkbox">.

Generates an <input type="checkbox">.


"Checkedness" depends upon whether the property array
specified contains a corresponding value as the one specified
for the multibox.
<html:multibox>

<html:errors>

Generates html to display any errors that may have occurred


during invocation of the validate() method.

<html:file>
<html:form>

Generates <form>.

<html:hidden>

There is a hidden element here which is invisible. :-)


Generates <input type="hidden">.

<html:html>

Generates <html>.

<html:image>

<html:img>

<html:link>

<html:password>

<html:radio>

A link to an external site


Generates an html link.

Generates <input type="password"> for use in collecting


information that should not be shown on-screen.
Credit

Debit

Generates a radio button (<input type="radio">).


Reset

<html:reset>

Generates <input type="reset">.


<html:rewrite>

55

<html:select>
<html:options>
<html:option>

<html:select>

Generates <select>.

<html:options>

Generates html for an entire list of <option>

tags.
<html:option>

Generates a single <option>.

Submit

<html:submit>

Generates <input type="submit"> to submit form data


entered by the user.
Name
<html:text>

Email Address
Generates <input type="text">.

<html:textarea>

Generates <textarea>.

The ActionForm class


The purpose of the ActionForm class is to contain and provide validation of the userinput data. This class is subclassed for application specific customization.
Here is a template for a customized ActionForm class with markers denoting where
special items should be located in the class with $ symbols.

56
package $PACKAGE_NAME$;
import
import
import
import
import

javax.servlet.http.HttpServletRequest;
org.apache.struts.action.ActionError;
org.apache.struts.action.ActionErrors;
org.apache.struts.action.ActionForm;
org.apache.struts.action.ActionMapping;

import $OTHER_PACKAGES_TO_IMPORT$;
public class $THE_NAME_OF_THIS_FORM_CLASS$ extends ActionForm {
// The attributes of the class should go here
// For example, private int age;
$PRIVATE_ATTRIBUTES_FOR_THIS_CLASS$
// The constructor method for this class
public $THE_NAME_OF_THIS_FORM_CLASS$() {
}
// The method to invoke when a reset occurs
public void reset(ActionMapping mapping, HttpServletRequest request) {
}
// The method providing validation of this classes attributes
public ActionErrors validate(ActionMapping mapping, HttpServletRequest
request) {
ActionErrors errors = new ActionErrors();
if
($SOME_CODE_TO_VALIDATE_AN_ATTRIBUTE_OF_THIS_CLASS_RETURNING_true_ON_SUCCESS$)
{
//
} else {
// Add an error to the errors to be returned that designates the
validation of the
// current attribute failed. The information will come from the
Application Resources.
errors.add("$THE_ATTRIBUTES_NAME$", new
ActionError("$SOME_KEY_FROM_THE_ApplicationResources.properties_FILE$"));
}
// Return the errors
return errors;
}
$ACCESSOR_AND_MUTATOR_METHODS_FOR_THE_ATTRIBUTES_OF_THIS_CLASS$
// For example,
// public int getAge() { return age; }
// public void setAge(int newAge) { age = newAge; }
}

57

The Action class


The purpose of the Action class is to perform the appropriate actions on the user input
gathered from the form.
Here is a template for a customized Action class with markers denoting where special
items should be located in the class with $ symbols.

package $PACKAGE_NAME$;
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import

java.util.Vector;
java.io.IOException;
java.lang.reflect.InvocationTargetException;
java.util.Locale;
java.util.Hashtable;
javax.servlet.RequestDispatcher;
javax.servlet.ServletException;
javax.servlet.http.HttpServletRequest;
javax.servlet.http.HttpSession;
javax.servlet.http.HttpServletResponse;
org.apache.struts.action.Action;
org.apache.struts.action.ActionError;
org.apache.struts.action.ActionErrors;
org.apache.struts.action.ActionForm;
org.apache.struts.action.ActionForward;
org.apache.struts.action.ActionMapping;
org.apache.struts.action.ActionServlet;
org.apache.struts.util.MessageResources;
org.apache.struts.util.PropertyUtils;

import $OTHER_PACKAGES_TO_IMPORT$;
public final class $THE_NAME_OF_THIS_ACTION_CLASS$ extends Action {
// The constructor method for this class
public $THE_NAME_OF_THIS_ACTION_CLASS$() {
}
// The method used for processing the user-input
public ActionForward perform(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
Locale locale = getLocale(request);
MessageResources messages = getResources();
HttpSession session = request.getSession();
$SOME_FORM_CLASS$ $SOME_FORM_CLASS_INSTANCE$ =
($SOME_FORM_CLASS$)form;

58

// ActionErrors errors = new ActionErrors();


$CODE_FOR_PROCESSING_USER_INPUT$
if ($PROCESSING_WAS_SUCCESSFUL$) {
} else {
return (mapping.findForward("$FAILURE_FORWARD_PAGE$"));
}
if (mapping.getAttribute() != null) {
if ("request".equals(mapping.getScope()))
request.removeAttribute(mapping.getAttribute());
else
session.removeAttribute(mapping.getAttribute());
}
return (mapping.findForward("$SUCCESS_FORWARD_PAGE$"));
}
}

SimpleStruts: a simple Struts application


(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.)
This example demonstrates the use of Struts for a simple web application using HTML
forms. The application will request a name from the user and will display that name on
the page containing the form.
The NameForm class will be used for storing the user input. The SetNameAction class
will receive an instance of the NameForm class containing the user input on which to
perform its action. An instance of the Name class will be stored in the user's session to
represent the name that was last entered.
Do this:
1. Create the directory structure. The root directory is SimpleStruts, and it has the
standard WEB-INF directory with classes inside, and
com/masslight/strutsExampleClasses inside that. It also has a lib directory
within WEB-INF, which is something we haven't seen before; we'll see in a minute
what goes there.

59

code/Chapter4/SimpleStruts
SimpleStruts
|
+-- index.jsp (*)
|
+-- build.xml (*)
|
+-- WEB-INF
|
+-- web.xml (*)
|
+-- struts-config.xml (*)
|
+-- struts-bean.tld (*)
|
+-- struts-form.tld (*)
|
+-- struts-html.tld (*)
|
+-- struts-logic.tld (*)
|
+-- struts-template.tld (*)
|
+-- struts.tld (*)
|
+-- app.tld (*)
|
+-- classes
|
|
|
+-- com
|
|
|
+-- masslight
|
|
|
+-- strutsExampleClasses
|
|
|
+-- ApplicationResources.properties (*)
|
|
|
+-- Name.java (*)
|
|
|
+-- NameForm.java (*)
|
|
|
+-- SetNameAction.java (*)
|
+-- lib
|
+-- struts.jar (*)
(*) denotes a file

2. Copy the Struts tag library descriptor files into WEB-INF. The files struts.tld,
struts-bean.tld, struts-form.tld, struts-html.tld, struts-logic.tld,

60
and struts-template.tld are available in the lib directory of your Struts
installation.
c:\j2ee\code\Chapter4\SimpleStruts\WEB-INF\> copy
c:\j2ee\struts\lib\struts*.tld .
c:\j2ee\struts\lib\struts-bean.tld
c:\j2ee\struts\lib\struts-form.tld
c:\j2ee\struts\lib\struts-html.tld
c:\j2ee\struts\lib\struts-logic.tld
c:\j2ee\struts\lib\struts-template.tld
c:\j2ee\struts\lib\struts.tld
6 file(s) copied.

3. Copy the Struts parser, struts.jar, into WEB-INF/lib/. This file is available in
the lib directory of your Struts installation
4. Create the tag descriptor library file for any custom tags you may use beyond the
Struts tags. In this case, the file defines no custom tags, but it's good practice to
have it in place, in case you need to add your own tags later.
code/Chapter4/SimpleStruts/WEB-INF/app.tld
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE taglib
PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN"
"http://java.sun.com/j2ee/dtds/web-jsptaglib_1_1.dtd">
<taglib>
<tlibversion>1.0</tlibversion>
<jspversion>1.1</jspversion>
<shortname>utility</shortname>
<info>
Empty tag library template
</info>
</taglib>

5. Create the struts-config.xml file.


There are three main sections to a struts-config.xml configuration file. They
are the "Form Bean Definitions" section, the "Global Forward Definitions"
section, and the "Action Mapping Definitions" section.
The NameForm class will be defined in the form bean definition section, where it
will receive a name for use in the "Action Mapping Definitions" section. The
global forward definition section defines a forward called "success".
A global forward is defined in the "Global Forward Definitions" section.
Whenever this forward is referenced, index.jsp will be displayed.

61
The "Action Mapping Definitions" is the most important section within the
configuration file. This section takes a form defined in the "Form Bean
Definitions" section and maps it to an action class.
Here is what the struts-config.xml configuration file will look like for this
application:
code/Chapter4/SimpleStruts/WEB-INF/struts-config.xml
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration
1.0//EN"
"http://jakarta.apache.org/struts/dtds/struts-config_1_0.dtd">
<struts-config>
<!-- ========== Form Bean Definitions
=================================== -->
<form-beans>
<!-- name form bean -->
<form-bean name="nameForm"
type="com.masslight.strutsExampleClasses.NameForm"/>
</form-beans>
<!-- ========== Global Forward Definitions
============================== -->
<global-forwards>
<forward name="success" path="/index.jsp"/>
</global-forwards>
<!-- ========== Action Mapping Definitions
============================== -->
<action-mappings>
<!-- Save user registration -->
<action path="/setName"
type="com.masslight.strutsExampleClasses.SetNameAction"
name="nameForm"
scope="request"
input="/index.jsp"/>
</action-mappings>
</struts-config>

6. Create the web.xml file.


The web.xml web application configuration file will need to define the servlet
ActionServlet, to which control will be transferred whenever an appropriate
URL pattern is accessed. The servlet is defined just as any other servlet will be

62
defined. The URL pattern is specified by a servlet mapping. For this application,
the URL pattern is any requested resource that ends with a .do extension.
In order to use the Struts tags, the .tld files describing the tags will need to be
included in the configuration file. The references to these tags are made just as
they were for our own custom tags in the previous chapter. The Struts framework
is simply a complex set of tag libraries (struts*.tld), with associated code
(struts.jar).
The web.xml configuration file should look like this:
code/Chapter4/SimpleStruts/WEB-INF/web.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
"http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
<web-app>
<!-- Action Servlet Configuration -->
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servletclass>
<init-param>
<param-name>application</param-name>
<paramvalue>com.masslight.strutsExampleClasses.ApplicationResources</paramvalue>
</init-param>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
<param-name>debug</param-name>
<param-value>2</param-value>
</init-param>
<init-param>
<param-name>detail</param-name>
<param-value>2</param-value>
</init-param>
<init-param>
<param-name>validate</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<!-- Action Servlet Mapping -->

63

code/Chapter4/SimpleStruts/WEB-INF/web.xml
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<!-- The Welcome File List -->
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<!-- Application Tag Library Descriptor -->
<taglib>
<taglib-uri>/WEB-INF/app.tld</taglib-uri>
<taglib-location>/WEB-INF/app.tld</taglib-location>
</taglib>
<!-- Struts Tag Library Descriptors -->
<taglib>
<taglib-uri>/WEB-INF/struts-bean.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-bean.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>/WEB-INF/struts-html.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-html.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>/WEB-INF/struts-logic.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-logic.tld</taglib-location>
</taglib>
</web-app>

7. The ApplicationResources.properties file provides resources that will be


used by any subclassed Struts classes (for example, SetNameAction). This
resources file provides a place to define prompts, labels that will display on
buttons, and other information that may change. By placing this information in the
ApplicationResources.properties file, recompiling any servlets used in the
application can be avoided, as well as encouraging separation of logic and
presentation.
The ApplicationResources.properties file looks like this:

64

code/Chapter4/SimpleStruts/WEBINF/classes/com/masslight/strutsExampleClasses/ApplicationResources.properties
button.save=Change name
button.reset=Reset
error.name.required=To change the name, a name must be entered
prompt.name=Enter name:
welcome.title=A Simple Application

8. Instances of the Name class are placed in the user sessions. Only one will exist in
any particular user session. It provides methods for accessing and mutating a
name.
Place the following inside the Name.java file:
code/Chapter4/SimpleStruts/WEBINF/classes/com/masslight/strutsExampleClasses/Name.java
package com.masslight.strutsExampleClasses;
import java.io.Serializable;
public final class Name implements Serializable {
private String name = null;
public String getName() {
return (this.name);
}
public void setName(String name) {
this.name = name;
}
public String toString() {
StringBuffer sb = new StringBuffer("Name[name=");
sb.append(name);
sb.append("]");
return (sb.toString());
}
}

9. The NameForm class stores and validates the user input. It provides methods for
accessing and mutating the data in the form. Notice how the validate method uses
information from the ApplicationResources.properties file.

65
Place the following in the NameForm.java file:
code/Chapter4/SimpleStruts/WEBINF/classes/com/masslight/strutsExampleClasses/NameForm.java
package com.masslight.strutsExampleClasses;
import
import
import
import
import

javax.servlet.http.HttpServletRequest;
org.apache.struts.action.ActionError;
org.apache.struts.action.ActionErrors;
org.apache.struts.action.ActionForm;
org.apache.struts.action.ActionMapping;

public final class NameForm extends ActionForm

private String action = "Set";


private String name = null;
public String getAction() {
return (this.action);
}
public void setAction(String action) {
this.action = action;
}
public String getName() {
return (this.name);
}
public void setName(String name) {
this.name = name;
}
public void reset(ActionMapping mapping, HttpServletRequest request) {
this.action = "Set";
this.name = null;
}
public ActionErrors validate(ActionMapping mapping, HttpServletRequest
request) {
ActionErrors errors = new ActionErrors();
if ((name == null) || (name.length() < 1))
errors.add("username", new ActionError("error.name.required"));
return errors;
}
}

10. The SetNameAction class has a perform method, which receives an instance of
the NameForm class containing the user input.

66
Although the perform method here operates directly on the form data, in a realworld application it should instead delegate this duty to a business logic object.
(We will see how this works in chapter 7, once we learn about EJBs.) The
perform method takes the name from the form and creates a new instance of the
Name class, which is placed inside the session. If a bean by that name is already
present in the session, it is replaced by the new instance.
Notice how the class uses this code:
return (mapping.findForward("success"));

to display the next JSP. A new forward could have been defined in the strutsconfig.xml called "failed" or "DEFCON1". If any of the code in the perform
method had failed, the user could be forwarded to the JSP page defined in the
forward definition.
Place the following code in the SetNameAction.java file:
code/Chapter4/SimpleStruts/WEBINF/classes/com/masslight/strutsExampleClasses/SetNameAction.java
package com.masslight.strutsExampleClasses;
import
import
import
import
import
import
import
import

java.io.IOException;
java.lang.reflect.InvocationTargetException;
java.util.Locale;
java.util.Hashtable;
javax.servlet.*;
javax.servlet.http.*;
org.apache.struts.action.*;
org.apache.struts.util.*;

public final class SetNameAction extends Action {


public ActionForward perform(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
Locale locale = getLocale(request);
MessageResources messages = getResources();
HttpSession session = request.getSession();
NameForm nameform = (NameForm)form;
String action = request.getParameter("action");
if (action == null)
action = "Set";
if (servlet.getDebug() >= 1)

67

code/Chapter4/SimpleStruts/WEBINF/classes/com/masslight/strutsExampleClasses/SetNameAction.java
servlet.log("SetNameAction:

Processing " + action + " action");

Name name = (Name)session.getAttribute("name");


ActionErrors errors = new ActionErrors();
String value = null;
value = nameform.getName();
if ("Set".equals(action)) {
name = null;
name = new Name();
name.setName(nameform.getName());
}
session.setAttribute("name", name);
if (mapping.getAttribute() != null) {
if ("request".equals(mapping.getScope()))
request.removeAttribute(mapping.getAttribute());
else
session.removeAttribute(mapping.getAttribute());
}
return (mapping.findForward("success"));
}
}

12. The view of the application is done with the JSP index.jsp. It represents the user
interface and allows the user to interact with the application.
Information from the ApplicationResources.properties file is used for the
page title, prompts, and the Submit button label. This is done with the
<bean:message> tag. The attribute "key" is used to specify the application
resource to lookup in the ApplicationResources.properties file.
Any errors that were found by the validate method of the NameForm class are
displayed by using the <html:errors/> tag.
The action attribute of the <form> tag is "setName". When the user clicks the
Submit button, the action defined for setName in struts-config.xml is
invoked.

68
A text field is created and associated with the property "name" in an instance of
the NameForm class by using the attribute property in the <html:text> tag. Two
optional attributes are used to define the length and the maximum characters to
accept.
A new tag is introduced from the Struts logic tag library. <logic:present>
checks to see if a bean whose name is specified with the name attribute exists. If a
user has previously entered a name and submitted it without any errors, then a
bean by that name will exist. Whenever the bean is found, the information in the
enclosed by the <logic:present> tags are included in the html that is generated.
Don't worry about this for now; the next chapter will discuss handling conditional
logic in Struts.
The <bean:write> tag is used to display the value of the property in the bean
specified by the "property" and "name" attributes.
Place the following inside the index.jsp file:
code/Chapter4/SimpleStruts/index.jsp
<%@
<%@
<%@
<%@
<%@

page language="java" %>


taglib uri="/WEB-INF/app.tld" prefix="app" %>
taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>

<html:html>
<head>
<title>
<bean:message key="welcome.title"/>
</title>
<html:base/>
</head>
<body>
<html:errors/>
<html:form action="/setName">
<html:hidden property="action"/>
<bean:message key="prompt.name"/>
<html:text property="name" size="16" maxlength="16"/>
<br></br>
<html:submit>
<bean:message key="button.save"/>
</html:submit>
<html:reset>
<bean:message key="button.reset"/>
</html:reset>
</html:form>
<hr></hr>
<logic:present name="name">
<font color="blue">
<bean:write name="name" property="name"/>

69

code/Chapter4/SimpleStruts/index.jsp
</font>
<hr></hr>
</logic:present>
</body>
</html:html>

13. ant all to compile, jar, and deploy.


14. Go to http://localhost:8080/SimpleStruts/index.jsp to test your
application

70

Exercises
Exercise 1. Extending our form
Add the following to the form and appropriate methods and variables to the Name,
SetNameAction, and NameForm classes to process the information correctly:

a set of radio buttons for selection of contentness (happy or sad)

71

a set of checkboxes to allow the user to select favorite types of music (rock,
gospel, folk, country, and classical)
a selection to choose an age range (18-24, 25-40, 41-67, 67-300)
a text-area for entering side notes about the person (5 rows and 40 columns)

Exercise 2. Think
What happens when the browser is closed and the site is visited once again? How can this
be avoided?

72

Chapter 5. Advanced Struts


Conditional Logic, Looping, Headers, and Cookies

Conditional tags
ConditionalStruts: using conditional tags in Struts
Repeating Logic
IteratorStruts: Repeating HTML using Struts
Accessing HTTP headers
HeaderStruts: Accessing HTTP Headers with Struts
Cookies
CookieStruts: Reading and Writing Cookies with Struts
Exercises

Goals

After completing this chapter, the student will be able to


use the conditional tags from the Struts framework.
use the iterate tag from the Struts framework.
handle HTTP headers using Struts.

handle cookies with Struts and with the response object.

In order to complete this chapter successfully, the student must have


an understanding of the request-response cycle.
an understanding of cookies.
Prerequisites
a basic understanding of Struts.

Objectives

a basic understanding of beans and tags.

This chapter is presented in order to provide the student with information


about the logic, cookie, and header tags of Struts.

Conditional tags
The logic:present tag
The logic:present tag checks to see if something exists. If it does exist, then the body
of the logic:present tag is executed, otherwise the body of the logic:present tag is
ignored entirely. It's one big fancy if statement.
logic:notPresent does the opposite, but
attributes) as the logic:notPresent tag.

works the same way (and has the same

73
logic:present most commonly checks for the presence
using the cookie, header, or name attributes. Remember

of a cookie, a header, or a bean


that an attribute in a scope is
considered a bean, so the presence of an attribute may be checked using logic:present,
as well.

<logic:present name="person">
<bean:write name="person" property="firstName"/>
...
...
</logic:present>

Attributes of the logic:present tag


value

property

cookie

Checks for the presence of a cookie with the specified name

header

Checks for the presence of a header with the specified name

name

Checks for the presence of a bean with the specified name

parameter

Checks for an instance of at least one parameter from the request

property

Checks for the presence of a non-null property of the bean with the name
specified in the name property

role

Checks to see if the currently authenticated user has the specified security role

scope

Specifies the scope of where to find the bean

user

Checks to see if the currently authenticated user has the specified name

Using comparison tags


Comparison tags allow two arguments to be compared; if the comparison evaluates to be
true, then the content contained in the tag will be executed. Comparison tags can be
equal, notEqual, greaterEqual, lessEqual,lessThan, or greaterThan. Later in this
chapter, the ConditionalStruts example will show how to use these more fully.
Attributes of comparison tags
value

value to be compared to a value from the cookie, header, parameter,


property or name property - required

cookie

name of cookie to compare to the value property

header

the name of the HTTP header to compare to the value property

name

the name of the bean whose value will be compared to the value
property - if the property attribute is also specified, then the value of
the property attribute will be compared to the value property

parameter

the name of the request parameter to compare to the value property

74

property

the property of the bean to compare to the value property - if this


attribute is used then the name attribute must be used as well

scope

the scope within which to search for the bean specified by name

ConditionalStruts: using conditional tags in Struts


This example demonstrates the use of the conditionals tags from the Struts framework.
The example is an application where the user must guess a number. We will use Struts
tags to implement conditional logic in the JSP to test whether the number is correct, too
high, or too low.
We will build on the example from the previous chapter, which asked for a name and then
displayed the name to the user. Note how little has to change: only index.jsp (to add the
conditional logic), ApplicationResources.properties (to change the labels displayed
to the user), and build.xml (to deploy the application under a different name).
Everything else remains the same.

web.xml

is the same because our basic application configurations have not

changed.

is the same because our Action mappings have not changed;


clicking 'Submit' still executes the same Action
(com.masslight.strutsExampleClasses.SetNameAction).
SetNameAction.java is the same, because the Action logic has not changed;
after clicking 'Submit', the user is unconditionally redirected to the same page
(index.jsp, mapped to the name "success" in struts-config.xml).
NameForm.java has not changed, because the structure of the data we store in and
retrieve from the HTML form has not changed; it still only have a single field,
"Name".
Name.java has not changed. That we will be asking the user to enter numeric data
in this field is not actually relevant; the internal representation of that data has not
changed, so Name.java is the same.
struts-config.xml

So, we will be reusing most of our code from the previous chapter, and only changing
what we need to, which is very little.
(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:
1. Duplicate the entire directory of the SimpleStruts example from the previous
chapter. Rename it to ConditionalStruts. Your directory structure should look
very familiar:

75

code/Chapter5/ConditionalStruts
ConditionalStruts
|
+-- index.jsp (*)
|
+-- build.xml (*)
|
+-- WEB-INF
|
+-- web.xml (*)
|
+-- struts-config.xml (*)
|
+-- app.tld (*)
|
+-- struts-bean.tld (*)
|
+-- struts-form.tld (*)
|
+-- struts-html.tld (*)
|
+-- struts-logic.tld (*)
|
+-- struts-template.tld (*)
|
+-- struts.tld (*)
|
+-- classes
|
|
|
+-- com
|
|
|
+-- masslight
|
|
|
+-- strutsExampleClasses
|
|
|
+-- ApplicationResources.properties (*)
|
|
|
+-- NameForm.java (*)
|
|
|
+-- Name.java (*)
|
|
|
+-- SetNameAction.java (*)
|
+-- lib
|
+-- struts.jar (*)
(*) denotes a file

2. Change the JSP file to include our new conditional logic:

76

code/Chapter5/ConditionalStruts/index.jsp
<%@ page language="java" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
<html:html>
<head>
<title>
<bean:message key="welcome.title"/>
</title>
<html:base/>
</head>
<body>
<!-- This will set the number to be guessed -->
<bean:define id="theNumber" value="7"/>
<html:errors/>
<html:form action="/setName">
<html:hidden property="action"/>
<bean:message key="prompt.name"/>
<html:text property="name" size="16" maxlength="16"/>
<br></br>
<html:submit>
<bean:message key="button.save"/>
</html:submit>
<html:reset>
<bean:message key="button.reset"/>
</html:reset>
</html:form>
<hr></hr>
<logic:present name="name">
<bean:write name="name" property="name"/>
<br></br>

%>" >

<!-- Equal To -->


<logic:equal name="name" property="name" value="<%= theNumber
<bean:message key="result.correct"/>
</logic:equal>

<logic:notEqual name="name" property="name" value="<%=


theNumber %>">
<!-- Less Than -->
<logic:lessThan name="name" property="name" value="<%=
theNumber %>" >
<bean:message key="result.lessThan"/>
</logic:lessThan>
<!-- Greater Than -->
<logic:greaterThan name="name" property="name" value="<%=
theNumber %>" >
<bean:message key="result.greaterThan"/>
</logic:greaterThan>

77

code/Chapter5/ConditionalStruts/index.jsp
</logic:notEqual>
<hr></hr>
</logic:present>
</body>
</html:html>

3. Change the labels in the properties file, and add the new messages we'll need for
this application:
code/Chapter5/ConditionalStruts/WEBINF/classes/com/masslight/strutsExampleClasses/ApplicationResources.properties
button.save=Guess the number
button.reset=Reset
error.name.required=To guess the number, a number must be entered
prompt.name=Enter number:
welcome.title=Number Guesser
result.correct=WOOHOO - ROCK ON!
result.lessThan=A little higher
result.greaterThan=A little lower

4. Change the project name and the .war file name in the Ant build script; everything
else is unchanged.
code/Chapter5/ConditionalStruts/build.xml
<project name="ConditionalStruts" default="dist" basedir=".">
<!-- set global properties for this build -->
<property environment="env"/>
<property name="top" value="."/>
<property name="src" value="."/>
<property name="build" value="build"/>
<property name="dist" value="dist"/>
<property name="war_dir" value="${dist}/lib"/>
<property name="war_file" value="${war_dir}/ConditionalStruts.war"/>
<property name="webinf" value="${top}/WEB-INF"/>
<property name="web.xml" value="${webinf}/web.xml"/>
<property name="classes" value="${webinf}/classes"/>
<property name="lib" value="${top}/WEB-INF/lib"/>
<property name="struts.jar" value="${env.STRUTS_HOME}/lib/struts.jar"/>
<property name="servlet.jar" value="$
{env.TOMCAT_HOME}/lib/servlet.jar"/>
<property name="deploy" value="${env.JBOSS_HOME}/deploy"/>

78

code/Chapter5/ConditionalStruts/build.xml
<target name="clean">
<!-- Delete our the ${build} and ${dist} directory trees -->
<delete dir="${build}"/>
<delete dir="${dist}"/>
<delete dir="${war_dir}"/>
</target>
<target name="init">
<!-- Create the build directory structure used by compile and dist
-->
<mkdir dir="${build}"/>
<mkdir dir="${dist}"/>
<mkdir dir="${war_dir}"/>
</target>
<target name="compile" depends="init">
<!-- Compile the java code from ${src} into ${build} -->
<javac
srcdir="${top}/${src}"
destdir="${build}"
classpath="${servlet.jar}:${struts.jar}"/>
</target>
<target name="dist" depends="compile">
<!-- Put everything in a war file -->
<war warfile="${war_file}" webxml="${web.xml}">
<!-- include all JSPs in root level, and all .properties files
anywhere -->
<fileset dir="${top}/${src}">
<include name="*.jsp"/>
<include name="**/*.properties"/>
</fileset>
<!-- include all tag libraries in WEB-INF, and all .xml config
files,
but not web.xml (that's handled separately) -->
<webinf dir="${webinf}">
<include name="*.tld"/>
<include name="*.xml"/>
<exclude name="web.xml"/>
</webinf>
<!-- include all libraries in WEB-INF/lib (like struts.jar) -->
<lib dir="${lib}"/>
<!-- include all compiled classes -->
<classes dir="${build}"/>
</war>
</target>
<target name="deploy">
<!-- Copy the war file to the JBoss deploy directory -->

79

code/Chapter5/ConditionalStruts/build.xml
<copy file="${war_file}" todir="${deploy}"/>
</target>
<target name="all" depends="clean,dist,deploy"/>
</project>

5. ant all to compile, jar, and deploy.


6. Go to http://localhost:8080/ConditionalStruts/index.jsp to test the
application

Repeating Logic
The logic:iterate tag is used to repeat over the section in the JSP file defined by the
<logic:iterate ..> and the </logic:iterate> tags. The iterate tag requires a
collection to iterate over. The tag iterates over all the elements of the collection, unless
the length property constrains the tag to a certain number of iterations. For each
iteration, the tag selects the next element in the collection and sends the value of the
element to a JSP bean with a name that is specified by the id property. Another Struts tag,
the bean:write tag can then be used to output the value of the element.
The most important properties of the logic:iterate tag are shown in the following
table. Following the table is an example that shows two ways to use the iterate tag,
though the way using the name attribute is recommended.

80
<logic:iterate>

properties

property

description

id

the name of a JSP bean that will contain the current element of the
collection for each iteration - required

collection

the value of a collection to be used, expressed as a String - please


look at the second index.jsp in the following example that uses
the collection property - one must use either the collection or
name element to provide the collection of elements

length

used to specify the maximum number of elements that will be


iterated through - must evaluate to a java.lang.Integer or to an
int

name

name of the JSP bean that contains the collection that will be
iterated over - one must use either collection or name to provide
the collection of elements

offset

the value, expressed as an integer or as a java.lang.Integer, of


where the iterator tag begins the iteration of the collection - the
default value is zero.

property

the name of the property of the bean

type

the fully qualified Java class type of an element of the collection

scope

the scope of the newly-created bean - page-wide scope is the


default

IteratorStruts: Repeating HTML using Struts


Do this:
1. Create the directory structure. The root directory is IteratorStruts, and it
should contain a WEB-INF with all the Struts libraries and configuration files
(copied from the previous examples). However, the class directory for this
example is com.masslight.actions, so create the directory structure
accordingly.
code/Chapter5/IteratorStruts
IteratorStruts
|
+-- index.jsp (*)
|
+-- build.xml (*)
|
+-- WEB-INF

81

code/Chapter5/IteratorStruts
|
+-|
+-|
+-|
+-|
+-|
+-|
+-|
+-|
+-|
+-|
|
|
|
|
|
|
|
|
+--

web.xml (*)
struts-config.xml (*)
app.tld (*)
struts-bean.tld (*)
struts-form.tld (*)
struts-html.tld (*)
struts-logic.tld (*)
struts-template.tld (*)
struts.tld (*)
classes
|
+-- com
|
+-- masslight
|
+-- actions
|
+-- SetListAction.java (*)
lib
|
+-- struts.jar (*)

(*) denotes a file

2. Create the JSP


code/Chapter5/IteratorStruts/index.jsp
<%@ page language="java" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<html:html>
<header>
<title>Iterator</title>
<html:base/>
</header>
<body>

82

code/Chapter5/IteratorStruts/index.jsp
<logic:present name="list">
<logic:iterate id="myCollectionElement" name="list">
Element Value: <bean:write name="myCollectionElement" /><br />
</logic:iterate>
</logic:present>
</body>
</html:html>

3. Create the Java file


code/Chapter5/IteratorStruts/WEBINF/classes/com/masslight/actions/SetListAction.java
package com.masslight.actions;
import
import
import
import
import
import
import
import
import

java.util.Vector;
java.io.IOException;
java.lang.reflect.InvocationTargetException;
java.util.Locale;
java.util.Hashtable;
javax.servlet.*;
javax.servlet.http.*;
org.apache.struts.action.*;
org.apache.struts.util.*;

public final class SetListAction extends Action {


// The constructor method for this class
public SetListAction() {
}
// This sets the list as a session bean
public ActionForward perform(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
HttpSession session = request.getSession();
java.util.ArrayList list = new java.util.ArrayList();
list.add("ONE");
list.add("TWO");
list.add("THREE");
session.setAttribute("list",list);
return (mapping.findForward("success"));
}

83

code/Chapter5/IteratorStruts/WEBINF/classes/com/masslight/actions/SetListAction.java
}

4. Create the Struts configuration file


code/Chapter5/IteratorStruts/WEB-INF/struts-config.xml
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration
1.0//EN"
"http://jakarta.apache.org/struts/dtds/strutsconfig_1_0.dtd">
<struts-config>
<!-- ========== Form Bean Definitions
=================================== -->
<!-- ========== Global Forward Definitions
============================== -->
<global-forwards>
<forward name="success" path="/index.jsp"/>
</global-forwards>
<!-- ========== Action Mapping Definitions
============================== -->
<action-mappings>
<!-- Save user registration -->
<action path="/setList"
type="com.masslight.actions.SetListAction" />
</action-mappings>
</struts-config>

5. Copy the Ant build script from the previous example. Change IteratorStruts to
HeaderStruts in the project name and .war file name. Everything else is
unchanged.
6. ant all
7. Go to http://localhost:8080/IteratorStruts/setList.do to test the
application. This is because there is no input in this application; everything
happens in the Action, defined in SetListAction.java. In order to see the output
of this Action, we need to invoke the Action directly, by going to the URL that is
mapped to the Action (in struts-config.xml).

84

Accessing HTTP headers


The HTTP request that a client sends to a server and the HTTP response that a server
sends to a client can both include multiple headers. Associated with each header is a
name and a value. A header can provide information about the hostname of the client, the
default human language setting on the machine, or anything else that can be contained in
a name and a value.
In JSPs and servlets, the java.servlet.http.HttpServletRequest class provides
access to the HTTP header information from the request and the
java.servlet.http.HttpServletResponse class provides access to the header
information for the response. The HTTPServletRequest and HTTPServletResponse
methods that allow access to HTTP header information are shown in the following tables.
The request and response objects are the instantiations of the HTTPServletRequest
and HTTPServletResponse classes in JSP pages. The important methods of the two
classes are listed below.
HTTPServletRequest

methods for reading incoming HTTP headers

return type

method name

description

long

getDateHeader(String
nameOfHeader)

returns the long value of the date

String

getHeader(String
nameOfHeader)

returns the value of the header with the name

Enumeration getHeaderNames()

nameOfHeader

returns an Enumeration of the names of all the


headers

Enumeration

getHeaders(String
nameOfHeader)

int

returns the int value of a header with the name


nameOfHeader - if the value cannot be returned as
getIntHeader(String an integer, then a NumberFormatException is
nameOfHeader)
thrown - if the request does not contain a header
with the name of nameOfHeader, then a -1 is
returned

HTTPServletResponse

returns all the values of a specific header

methods for setting outgoing HTTP headers

return
type

method name

description

void

addDateHeader(String
nameOfHeader, long
date)

adds a header with the name nameOfHeader and the


date, expressed in the number of milliseconds since
1-1-1970 GMT

void

addHeader(String
nameOfHeader, String
value)

adds a header with the name nameOfHeader and the


value named value

85

void

addIntHeader(String
nameOfHeader, int
value)

adds a header with the name nameOfHeader and the


int value named value.

boolean

containsHeader(String
nameOfHeader)

returns true if there is header named nameOfHeader


that exists, otherwise returns false

void

setDateHeader(String
nameOfHeader, long
date)

sets the time value for a specified time, expressed in


the number of milliseconds since January First, 1970
GMT, for a header named nameOfHeader

void

setHeader(String
nameOfHeader, String
value)

sets a header with the name nameOfHeader to the


value value.

void

setIntHeader(String
nameOfHeader, int
value)

sets a header with the name nameOfHeader to the int


value value.

Common header names and possible associated values


Cache-Control = no-cache
Cookie = $Version=1; JSESSIONID=rkoo4wrr11; $Path="/AllHeaders"
Host = localhost:8080
Accept = text/html, image/png, image/jpeg, image/gif, image/x-xbitmap,
*/*
User-Agent = Mozilla/5.0 (Windows 2000; U) Opera 6.0 [en]
Accept-Language = en
Accept-Encoding = deflate, gzip, x-gzip, identity, *;q=0
Pragma = no-cache
TE = deflate, gzip, chunked, identity, trailers
Accept-Charset = windows-1252;q=1.0, utf-8;q=1.0, utf-16;q=1.0, iso8859-1;q=0.6, *;q=0.1
Connection = Keep-Alive, TE

Accessing Headers with Struts


Struts provides a tag called bean:header that can read the value of an HTTP header.
Below is a table describing the different properties of the bean:header tag, followed by
an example that demonstrates how to use the tag.
bean:header

tag properties

property name

description

id

the name of the scripting variable with page scope - required

messages

this property requires the name of an application scope message


variable - this property is not discussed in this chapter

multiple

if there might be more than one header with the same name, this
property should not be null - the scripting variable will then hold an
array of headers, if necessary

name

the name of the header - required

86
property name
value

description
the value to return if no header exists with the name specified in the
name property

HeaderStruts: Accessing HTTP Headers with Struts


Do this:
1. Create the directory structure, with HeaderStruts as the root directory. Create
the WEB-INF directory, and copy all the Struts libraries and configuration files
from the previous example. However, there is no JavaBean in this example, so
you can skip the classes directory altogether.
code/Chapter5/HeaderStruts
HeaderStruts
|
+-- index.jsp (*)
|
+-- build.xml (*)
|
+-- WEB-INF
|
+-- web.xml (*)
|
+-- app.tld (*)
|
+-- struts-bean.tld (*)
|
+-- struts-config.xml (*)
|
+-- struts-form.tld (*)
|
+-- struts-html.tld (*)
|
+-- struts-logic.tld (*)
|
+-- struts-template.tld (*)
|
+-- struts.tld (*)
|
+-- lib
|
+-- struts.jar (*)
(*) denotes a file

2. Create the JSP file

87

code/Chapter5/HeaderStruts/index.jsp
<%@ page language="java" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
<html>
<head>
<title>
Headers with Struts
</title>
</head>
<body>
<logic:present header="User-Agent">
<bean:header id="theheader" name="User-Agent"/>
You are currently running <bean:write name="theheader"/>
</logic:present>
</body>
</html>

3. Create the web.xml file. Since this application has no Actions or JavaBeans, this
file is much simpler than the previous examples.
code/Chapter5/HeaderStruts/WEB-INF/web.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
"http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
<web-app>
<taglib>
<taglib-uri>/WEB-INF/struts-bean.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-bean.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>/WEB-INF/struts-logic.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-logic.tld</taglib-location>
</taglib>
</web-app>

4. Copy the build.xml file from the previous example. Change IteratorStruts to
HeaderStruts.
5. ant all to compile, build, and deploy.
6. Go to http://localhost:8080/HeaderStruts/index.jsp to test your
application.

88

Cookies
Reading cookies
The bean:cookie tag allows the developer to access cookies and their values. Below is a
table that describes the different properties of the tag. The values of id, multiple, name,
and value can all be accessed from the scripting bean that represents the cookie. The
message property is not discussed in this chapter. Following the table is an example that
demonstrates how to access cookie information using Struts. Remember, it's good to
check if the cookie is present before using the bean:cookie tag.
bean:cookie

tag properties

property name

description

id

the name of the scripting variable with page scope - required

messages

this property requires the name of an application scope message


variable - this property is not discussed in this chapter

multiple

if there might be more than one cookie with the same name, this
property should not be null - the id scripting variable will then hold an
array of cookies

name

the name of the cookie - required

value

the value to return if no cookie exists with the name specified in the
name property

Writing cookies
Using Struts, cookies cannot be sent with the response to the client. To send a cookie with
the response, a javax.servlet.http.Cookie object must be added to the response
object. The cookie is then added to the client's cookie library and may be included in the
clients requests, depending on how the cookie was set. The response object was
discussed in chapter two.
Properties of the Cookie class
property

description

name

The name of the Cookie object

value

The value of the cookie

path

The path on the server where the browser will return the Cookie object

maxAge

The maximum length of time that the cookie will persist in seconds a value of -1 means that the cookie will persist until shut down

Here is a sample Java snippet that creates a new cookie:

89
String newValue = "" + System.currentTimeMillis();
info = new Cookie("MyCookie", newValue);
info.setMaxAge(60);
info.setPath("/");
response.addCookie(info);

CookieStruts: Reading and Writing Cookies with Struts


Do this:
1. Duplicate the directory with used in ConditionalStruts and rename it to
CookieStruts.
code/Chapter5/CookieStruts
CookieStruts
|
+-- index.jsp (*)
|
+-- build.xml (*)
|
+-- WEB-INF
|
+-- web.xml (*)
|
+-- struts-config.xml (*)
|
+-- app.tld (*)
|
+-- struts-bean.tld (*)
|
+-- struts-form.tld (*)
|
+-- struts-html.tld (*)
|
+-- struts-logic.tld (*)
|
+-- struts-template.tld (*)
|
+-- struts.tld (*)
|
+-- lib
|
|
|
+-- struts.jar (*)
|
+-- classes
|
+-- com
|
+-- masslight
|

90

code/Chapter5/CookieStruts
+-- strutsExampleClasses
|
+-- ApplicationResources.properties (*)
|
+-- NameForm.java (*)
|
+-- Name.java (*)
|
+-- SetNameAction.java (*)
(*) denotes a file

2. Create the JSP


code/Chapter5/CookieStruts/index.jsp
<%@ page language="java" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
<html:html>
<head>
<title>
<bean:message key="welcome.title"/>
</title>
<html:base/>
</head>
<body>
<!-- This will set the number to be guessed -->
<bean:define id="theNumber" value="7"/>
<html:errors/>
<html:form action="/setName">
<html:hidden property="action"/>
<bean:message key="prompt.name"/>
<html:text property="name" size="16" maxlength="16"/>
<br>
<html:submit>
<bean:message key="button.save"/>
</html:submit>
<html:reset>
<bean:message key="button.reset"/>
</html:reset>
</html:form>
<hr>
<logic:present cookie="name">
<bean:cookie id="namecookie" name="name"/>

91

code/Chapter5/CookieStruts/index.jsp
<bean:write name="namecookie" property="name"/>
<bean:write name="namecookie" property="value"/>
<br>
<!-- Equal To -->
<logic:equal name="namecookie" property="value" value="<%=
theNumber %>" >
<bean:message key="result.correct"/>
</logic:equal>
<logic:notEqual name="namecookie" property="value" value="<%=
theNumber %>">
<!-- Less Than -->
<logic:lessThan name="namecookie" property="value" value="<%=
theNumber %>" >
<bean:message key="result.lessThan"/>
</logic:lessThan>
<!-- Greater Than -->
<logic:greaterThan name="namecookie" property="value" value="<
%= theNumber %>" >
<bean:message key="result.greaterThan"/>
</logic:greaterThan>
</logic:notEqual>
<hr>
</logic:present>
</body>
</html:html>

3. Open build.xml and change the references in the project name and .war file from
ConditionalStruts to CookieStruts.
4. ant all to compile, jar, and deploy
5. Go to http://localhost:8080/CookieStruts/index.jsp to test the
application

Exercises
Exercise 1. Show all headers
Make an application that shows all the headers included in the request.
Exercise 2. Reload game
Make an application where the user tries to "hit" or reload the page when the second hand
of the system clock is even. The user wins a game every time he or she reloads the page
when there is an even amount of seconds. The application will have one cookie that keeps
track of how many times the user has "won" and another cookie that remembers how

92
many times the user has "played". The application will display the winning percentage of
the user, the number of games played, and the number of games won.

Exercise 3. Tables
Make an application with a web page that displays a table. The table should have at least
three columns and at least three rows. Each column should have a heading. The display of
the information should use an HTML <table>. Use a two-dimensional array to store the
data internally within a JavaBean. The display should look similar to this:
Name

Favorite Food

Quote

Joey

Pizza

Da Bulls

Mikey

Ice Cream

Da Bears

Louie

Tomatoes

Da Cubs

Chapter 6. Enterprise JavaBeans


Abstracting database objects

Goals

After completing this chapter, the student will be able to


explain the basic conceptual models of Enterprise JavaBeans.
use an Enterprise JavaBean.
implement session Enterprise JavaBeans.

implement entity Enterprise JavaBeans.

Prerequisites

In order to complete this chapter successfully, the student must have a


basic knowledge of databases.

Objectives

The purpose of this chapter is to provide an overview of EJBs, and


information about session and entity EJBs.

93

What Are Enterprise JavaBeans?


Enterprise JavaBeans (EJBs) are a part of the Java 2 Enterprise Edition specifications.
EJBs provide developers with a distributed, object-oriented, component-based
architecture. Developers commonly use an EJB to represent a business object or a table in
a database. Part of an application can be built by creating some EJBs to model the
different tables in the database and other EJBs to handle the non-persistent business logic.
In terms of the model-view-controller model, EJBs can be used to represent the model
portion of an application.
The EJB Architecture
The basic EJB architecture is composed of an EJB server, EJB containers, EJB clients,
Enterprise JavaBeans, and a helper directory and naming service (such as JNDI).
Enterprise JavaBeans reside within an EJB container and EJB containers reside in the
EJB server. The client does not directly invoke methods of the EJB, instead the container
acts as an intermediary between the EJB and the client.

Composed of server software, the EJB server is analogous to the Object


EJB servers Request Broker (ORB) from the CORBA world. The server provides a way
for the client to transact with the EJBs. It also provides naming services.
EJB
containers

Located inside the EJB server, the EJB containers provide a means for
invoking an EJBs methods. An EJB client can send a request to invoke a
method on an EJB to the EJB container. The EJB container invokes the
appropriate method on the EJB. Containers provide support for security,
object persistence, resource pooling, and transaction processing.

EJB clients find EJBs through a naming and directory service. The client
EJB clients then sends a message to an EJB container, which determines the EJB method
that the client wants to invoke.
Enterprise
JavaBeans

Enterprise JavaBeans can be session beans, entity beans, or message-driven


beans. We will discuss these more in the next section. EJBs reside within

94
EJB containers.
Directory
service

The directory/naming service provides the ability to locate any object


registered with the directory/naming service. It provides a mapping between
a given name of an entity and, in the case of EJBs, a stub that corresponds to
the home interface of an Enterprise JavaBean (this will be discussed later in
the chapter).

Types of Enterprise JavaBeans


Session Beans
Session beans are used to represent non-persistent business logic. They almost always
have an association with a particular client. If the container is shutdown, the session
beans running are lost. Session beans can access and modify persistent data, though entity
beans are usually more suitable for working with persistent data.
Entity Beans
In general, developers use entity beans to represent persistent business logic. Entity beans
can be shared amongst multiple clients and persist if the client is destroyed or if the
server or container is shutdown. Entity beans commonly represent tables of a database.
The properties of a bean class represent the different columns of the table. When an
instance of an entity EJB is created, the instance can be thought of as temporarily
representing a row in the table.
Message-driven Beans
Message-driven beans are an added feature of the EJB 2.0 specification. They are not
discussed in this chapter. A message-driven bean's primary ability is to execute some
section of code upon receiving a receipt from a single client message. They are stateless
and therefore non-persistent. Also, message-driven EJBs do not survive the crash of an
EJB container.
Passivation/Activation
An EJB container can manage its primary memory allocation by passivating and
activating the EJBs that it manages. Passivation saves the state of a bean to persistent
storage, and swaps it out of memory. Activation is just the opposite of passivation: the
state of a bean is restored from persistent storage and is swapped into memory. The
processes of passivation and activation apply to stateful session beans and all entity
beans. Before the EJB is passivated and after the EJB is activated, the container calls the
ejbPassivate() or ejbActivate(), respectively, of the EJB's bean class so that the EJB
can perform any necessary setup to handle the EJB's change of state.
Persistence
Stateful session beans and entity beans are persistent. The EJB container or the bean class
of the EJB provides the necessary functionality to support the persistence of the bean's
state. To save a bean, we can get a javax.ejb.Handle object for the bean by invoking
the EJB's getHandle() method. Restoration is accomplished by invoking the
getEJBObject() method on the Handle object.

95
The four basic components of an EJB
Introduction
The four main components of an EJB are the home interface, the remote interface, the
bean class, and the deployment descriptor.
The home interface
The home interface declares the methods for creating, finding, and destroying instances
of EJBs. The methods declared in the home interface must correspond to certain methods
implemented in the bean class.
The remote interface
The remote interface declares the methods that contain the EJB's business logic. The
methods declared in the remote interface must be implemented in the bean class.

The bean class


The bean class provides the logic of the bean. It must implement the methods declared in
the remote interface and must implement methods corresponding to the methods in the
home interface.
The deployment descriptor
The deployment descriptor is always called ejb-jar.xml.
The deployment descriptor describes EJB(s) to the EJB container. In this chapter we will
be deploying only one EJB at a time, so there will be only one EJB described in each
deployment descriptor. With JBoss, the .jar file of a single EJB can be deployed directly
to the EJB container. This is the method of deployment that will be taken in this chapter,
though normally an EJB would need to be deployed as part of an enterprise application
archive (.ear). We will see how to create an .ear file in the next chapter.
Naming the components of an EJB
If the EJB is named Demo, then the remote interface should be Demo.java, the home
interface should be DemoHome.java, the bean class should be either DemoBean.java or
DemoEJB.java. The deployment descriptor should always be ejb-jar.xml.

96

element

the home interface

the remote interface

the bean class

XML deployment
descriptor

location in EJB
.jar file
A Java interface
used for creating new enterprise beans,
located in a
locating existing enterprise beans, and
package in the
destroying enterprise beans
EJBs jar file
A Java interface
used for declaring the business methods of located in a
the enterprise bean
package in the
EJBs jar file
A Java class
used for encapsulating the application logic
located in a
of the bean and implementing the business
package in the
methods defined in the remote interface
EJBs jar file
Named ejbresides in an XML configuration file that
describes the enterprise bean(s) to the EJB jar.xml, and
in the
server using a deployment tool eliminates located
META-INF
many of the steps needed to create the
directory of the
deployment descriptor by hand
EJBs jar file
description

Deploying an EJB
In order for a session or an entity EJB to be deployed, four parts are needed: the remote
interface, the home interface, the bean class, and the deployment descriptor. The two
interfaces and the bean class are placed in a package and the deployment descriptor is

97
placed in the META-INF directory. The package and the META-INF/ejb-jar.xml are
jarred into an EJB .jar file. When the EJB is deployed, the four parts reside in the EJB
container.

The EJB Specification


The most current versions of the EJB specification can be found at Suns web site. A PDF
or Postscript version of the specification can be downloaded at
http://java.sun.com/products/ejb/docs.html.

How EJBs Work


How the client interacts with an EJB
When the EJB is deployed by the container, the container registers the given name of the
home interface to the naming directory. When a client requests the given name of the
home interface, the naming directory returns a stub for the home interface. The home stub
does not actually interact with the home interface at all. Instead, the home stub interacts
with the container's implementation of the home interface. The client uses the home stub
to call the create, remove, and/or find methods declared by the home interface. When a
create or find method is called successfully, the container will create an instance of the
bean class and the home implementation will return a remote stub, which corresponds to
an implementation of the remote interface. The client can then use the remote stub to
make calls to the EJB.
What are stubs?
A stub is a part of Remote Method Invocation (RMI) technology. A client can use a stub
to remotely invoke exposed methods of an object that exists in a location separate from
the client. The stub will receive the return value from the method invoked from the
remote object and pass the value to the client, if a value is returned.

98

Interacting with an EJB


An EJB client must perform all the client's actions outlined in the previous diagram. Each
necessary line of code is explained.
Set up the naming provider - values are for JBoss/Tomcat
System.setProperty("java.naming.factory.initial",
"org.jnp.interfaces.NamingContextFactory");
System.setProperty("java.naming.provider.url", "localhost:1099");

Get a naming context - in a try/catch statement


InitialContext jndiContext = new InitialContext();

Get a reference to the home stub - in a try/catch statement


Object reference = jndiContext.lookup("EJBName");

Get the home stub - in a try/catch statement

99
com.masslight.ExampleEJB.Home home = (com.masslight.ExampleEJB.Home)
PortableRemoteObject.narrow (reference,
com.masslight.ExampleEJB.Home.class);

Get the remote stub - in a try/catch statement


com.masslight.ExampleEJB.Remote remote = home.create();

Invoke a business method from an instance of the bean class - in a try/catch statement
remote.businessMethodOne(...);

EJB Deployment Descriptors


A deployment tool should be used to create the deployment descriptor but it is important
to understand how to create a basic deployment descriptor by hand. For a more thorough
description go to http://java.sun.com/dtd/ejb-jar_2_0.dtd.
Version and document tags
This pair of tags is required at the top of all deployment descriptors.
<?xml version="1.0" encoding="Cp1252"?>
<!DOCTYPE ejb-jar PUBLIC '-//Sun Microsystems, Inc.//DTD Enterprise
JavaBeans 2.0//EN' 'http://java.sun.com/dtd/ejb-jar_2_0.dtd'>

The ejb-jar tags


These tags should follow the version and document tags. A deployment descriptor can
describe more than one EJB. In this chapter's examples, only one EJB is described per
deployment descriptor. The session tag is used to describe a session EJB and the entity
tag is used to describe an entity EJB. display-name specifies a name that is intended to
be displayed by tools.
<ejb-jar>
<display-name>ExampleEJBs</display-name>
<enterprise-beans>
...
<session>
...
</session>
...
<entity>
...
</entity>
...
</enterprise-beans>
</ejb-jar>

100
The name of the bean
These tags should reside within both session and entity tags. display-name specifies a
name that is intended to be displayed by tools. ejb-name is the name that will be
registered with the directory service - this is the name that will be used by the EJB client
to locate the EJB.
<display-name>Hello</display-name>
<ejb-name>Hello</ejb-name>

The classes of the bean


These tags should reside within both session and entity tags. The classes used for the
home interface, the remote interface, and the bean class need to be declared. All three
should be fully qualified class names.
<home>com.masslight.HelloEJBClasses.HelloHome</home>
<remote>com.masslight.HelloEJBClasses.Hello</remote>
<ejb-class>com.masslight.HelloEJBClasses.HelloBean</ejb-class>

Session bean-specific tags


These tags should reside within a session tag. session-type should be either Stateless
or Stateful. transaction-type should be Bean.
<session-type>Stateless</session-type>
<transaction-type>Bean</transaction-type>

Entity bean-specific tags


These tags should reside within an entity tag. persistence-type should be Bean. primkey-class should be the fully qualified Java class of the primary key of the bean class.
reentrant should be False.
<persistence-type>Bean</persistence-type>
<prim-key-class>java.lang.Integer</prim-key-class>
<reentrant>False</reentrant>

Session Beans
Stateful versus Stateless

101
Session beans can be either stateless or stateful. A stateful session bean can have
properties with accessor and mutator methods and most importantly, must declare that it
is a Stateful Session Bean in the deployment descriptor. Stateless beans, on the other
hand, must declare themselves in the deployment descriptor file as a Stateless
Session Bean. If a session bean is declared as stateless, then EJB container will treat it
as such.
Stateless session beans have no internal state. For this reason, there is no need for them to
be passivated. Stateful session beans do have an internal state and therefore need to be
able to handle passivation and activation. A stateless session bean can be pooled to
service multiple clients, while a stateful session bean can associate with only one client at
a time.
Stateless Session Bean
has no properties with accessor or mutator
methods in the bean class
declared Stateless Session Bean in the
deployment descriptor
multiple clients per instance of a bean

Stateful Session Bean


has properties with accessor and mutator
methods in the bean class
declared Stateful Session Bean in the
deployment descriptor
single client per instance of a bean

Implementing a Session Bean


Session beans require a home interface, a remote interface, a bean class, and an XML
deployment descriptor. All four parts need to follow certain guidelines in order to be
correctly deployed by the EJB container. The home interface extends the
javax.ejb.EJBHome interface and the remote interface extends the
javax.ejb.EJBObject interface. The bean class needs to implement the
javax.ejb.SessionBean interface, which the EJB specification defines as the
following, along with its superclass, javax.ejb.EnterpriseBean:
interface javax.ejb.EnterpriseBean
public interface EnterpriseBean extends java.io.Serializable {}
interface javax.ejb.SessionBean
public interface SessionBean extends EnterpriseBean {
public abstract void ejbActivate() throws java.rmi.RemoteException;
public abstract void ejbPasivate() throws java.rmi.RemoteException;
public abstract void ejbRemove() throws java.rmi.RemoteException;
public abstract void setSessionContext(SessionContext ctx) throws
java.rmi.RemoteException;
}

102
Below are tables detailing the necessary elements of the remote interface, the home
interface, and the bean class of stateless and stateful session beans.
home interface extends javax.ejb.EJBHome

method name

returns

create() throws
java.rmi.RemoteException,
javax.ejb.CreateException

the stub
for the
remote
interface

Description
When the container receives this call, it
creates an instance of the session bean the create method can include
parameters, as long as each create
method declaration matches the
implementation of an ejbCreate method
in the bean class. There must be at least
one create method, but there can be more
than one. Required.

remote interface extends javax.ejb.EJBObject

method name

exampleMethod(int
GNPofFranceInBillionsOfPounds);

returns description
All methods declared in this interface are
specific to a particular EJB. Each method
declared here must also be implemented
in the bean class. Note: this is an
int
example method only used to show that
methods declared in the remote interface
must be implemented in the bean class.
Not Required.

bean class implements javax.ejb.SessionBean

method name
ejbCreate() throws
javax.ejb.CreateException

ejbRemove()

ejbActivate()

returns Description
Must correspond to the create method
declared in the home interface. The
void container calls the corresponding
ejbCreate method after a client has called
the create method. Required.
The container calls this method to alert the
instance of the bean that it is going to be
void destroyed. The instance of the bean can
then clean up before being removed.
Required.
void The container calls this method to alert the
instance of the bean that it is being
activated so that the instance of the bean
can respond accordingly, such as restoring

103

ejbPassivate()

void

setSessionContext()

void

exampleMethod(int
GNPofFranceInBillionsOfPounds)

int

network connections and handles.


Required.
The container calls this method to alert the
instance of the bean that it has been
passivated so that the instance of the bean
can respond accordingly, such as releasing
network connections and handles.
Required.
The container calls this method after the
bean has been created but before any of the
ejbCreate methods have been called. The
bean is responsible for storing the
javax.ejb.SessionContext object.
Required.
This example method would need to be
implemented in the bean class if it is
declared in the remote interface. Not
Required

Hello: a simple EJB application


(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:
1. Create the directory structure. The root directory should be Hello. Within that, we
will have two separate directories, HelloClient and HelloEJB. HelloClient
looks like the web applications we've been buildin throughout the course: it has
JSP files, a WEB-INF directory with the Struts framework, and a classes directory
with nested directories to represent the two packages we'll be using:
com.masslight.actions and com.masslight.beans.
2. Copy the Struts framework into Hello/HelloClient/WEB-INF.
code/Chapter6/Hello/HelloClient
HelloClient
|
+-- build.xml (*)
|

104

code/Chapter6/Hello/HelloClient
+-- index.jsp (*)
|
+-- WEB-INF
|
+-- web.xml (*)
|
+-- app.tld (*)
|
+-- struts-bean.tld (*)
|
+-- struts-config.xml (*)
|
+-- struts-form.tld (*)
|
+-- struts-html.tld (*)
|
+-- struts-logic.tld (*)
|
+-- struts-template.tld (*)
|
+-- struts.tld (*)
|
+-- classes
|
|
|
+-- com
|
|
|
+-- masslight
|
|
|
+-- beans
|
|
|
|
|
+-- HelloClientBean.java (*)
|
|
|
+-- actions
|
|
|
+-- GetBeanAction.java (*)
|
+-- lib
|
+-- struts.jar (*)
(*) denotes a file

3. Create the JSP in the HelloClient directory (not the root Hello directory!)
code/Chapter6/Hello/HelloClient/index.jsp
<%@ page language="java" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>

105

code/Chapter6/Hello/HelloClient/index.jsp
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<html:html>
<head>
<title>
EJB example
</title>
<html:base/>
</head>
<body>
<logic:present name="greeting">
<bean:write name="greeting"/>
</logic:present>
</body>
</html:html>

4. Create the web.xml file in HelloClient/WEB-INF/. Nothing unusual here; we've


seen all this before.
code/Chapter6/Hello/HelloClient/WEB-INF/web.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
"http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
<web-app>
<!-- Action Servlet Configuration -->
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servletclass>
<init-param>
<param-name>application</param-name>
<param-value>com.masslight.ApplicationResources</param-value>
</init-param>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
<param-name>debug</param-name>
<param-value>2</param-value>
</init-param>
<init-param>
<param-name>detail</param-name>
<param-value>2</param-value>
</init-param>
<init-param>

106

code/Chapter6/Hello/HelloClient/WEB-INF/web.xml
<param-name>validate</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<!-- Action Servlet Mapping -->
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<!-- Struts Tag Library Descriptors -->
<taglib>
<taglib-uri>/WEB-INF/struts-bean.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-bean.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>/WEB-INF/struts-html.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-html.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>/WEB-INF/struts-logic.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-logic.tld</taglib-location>
</taglib>
</web-app>

5. Create the struts-config.xml file. Again, this should look very familiar. It's not
identical to previous examples, but it's pretty close. The action path is different,
and points to a different classname of JavaBean, but that's about it.
code/Chapter6/Hello/HelloClient/WEB-INF/struts-config.xml
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration
1.0//EN"
"http://jakarta.apache.org/struts/dtds/struts-config_1_0.dtd">
<struts-config>
<!-- ========== Global Forward Definitions
============================== -->
<global-forwards>
<forward name="success" path="/index.jsp"/>

107

code/Chapter6/Hello/HelloClient/WEB-INF/struts-config.xml
</global-forwards>
<!-- ========== Action Mapping Definitions
============================== -->
<action-mappings>
<!-- Save user registration -->
<action path="/getEJB"
type="com.masslight.actions.GetBeanAction"

/>

</action-mappings>
</struts-config>

6. Create the Struts Action class for this JSP. This is where things start to get
interesting. In previous examples, this was where we did all the work. But here,
the Action class is calling the HelloClientBean and telling it to do the work.
code/Chapter6/Hello/HelloClient/WEBINF/classes/com/masslight/actions/GetBeanAction.java
package com.masslight.actions;
import
import
import
import
import
import
import
import
import

java.util.Vector;
java.io.IOException;
java.lang.reflect.InvocationTargetException;
java.util.Locale;
java.util.Hashtable;
javax.servlet.*;
javax.servlet.http.*;
org.apache.struts.action.*;
org.apache.struts.util.*;

import com.masslight.beans.HelloClientBean;
public final class GetBeanAction extends Action {
// The constructor method for this class
public GetBeanAction() {
}
// This sets the list as a session bean
public ActionForward perform(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
HttpSession session = request.getSession();

108

code/Chapter6/Hello/HelloClient/WEBINF/classes/com/masslight/actions/GetBeanAction.java
HelloClientBean clientbean = new HelloClientBean();
clientbean.startup();
session.setAttribute("greeting",
clientbean.sayGreeting("Hello"));
return (mapping.findForward("success"));
}

7. Create the client bean. Note that this doesn't do any real work either (and by "real
work", I mean "business logic"); instead, it gets a reference to the EJB and tells it
to do the work.
code/Chapter6/Hello/HelloClient/WEBINF/classes/com/masslight/beans/HelloClientBean.java
package com.masslight.beans;
import
import
import
import

javax.naming.*;
java.util.Hashtable;
javax.rmi.PortableRemoteObject;
javax.ejb.*;

public class HelloClientBean


{
com.masslight.HelloEJBClasses.Hello hello = null;
com.masslight.HelloEJBClasses.HelloHome home = null;
public HelloClientBean() {
}
/**

This method does all the work. It creates an instance of the


hello EJB on
the EJB server, and calls its `sayHello()' method, then prints
the result the returned String.
*/
public void startup () {
// Set up the naming provider; this may not always be necessary,
depending
// on how your Java system is configured.
System.setProperty("java.naming.factory.initial",
"org.jnp.interfaces.NamingContextFactory");
System.setProperty("java.naming.provider.url",
"localhost:1099");
// A single `try' block is not an ideal way

to do exception

109

code/Chapter6/Hello/HelloClient/WEBINF/classes/com/masslight/beans/HelloClientBean.java
handling, but it
// is easier to read code without all the catch blocks
try
{
// Get a naming context
InitialContext jndiContext = new InitialContext();
System.out.println("Got context \n");
// Get a reference to the hello Bean
Object ref = jndiContext.lookup("Hello");
System.out.println("Got reference \n");
// Get a reference from this to the Bean's Home interface
home = (com.masslight.HelloEJBClasses.HelloHome)
PortableRemoteObject.narrow (ref,
com.masslight.HelloEJBClasses.HelloHome.class);
// Create an hello object from the Home interface
hello = home.create();
}
{
}

catch(Exception e)
System.out.println(e.toString());

}
public String sayGreeting(String greeting) {
String output = new String("");
try {
output += hello.sayHello(greeting);
} catch(Exception e)
{
System.out.println(e.toString());
}
return(output);
}
}

8. Create a directory structure to hold the EJB code. The root directory, HelloEJB,
should be in the same directory as the HelloClient directory we created for the
client code. Within that, we need a META-INF directory for the EJB descriptor, and
a directory hierarchy to represent the path to the EJB classes themselves, which is
com.masslight.HelloEJBClasses.
code/Chapter6/Hello/HelloEJB
HelloEJB
|
+-- build.xml (*)

110

code/Chapter6/Hello/HelloEJB
|
+-- META-INF
|
|
|
+-- ejb-jar.xml (*)
|
+-- com
|
+-- masslight
|
+-- HelloEJBClasses
|
+-- HelloBean.java (*)
|
+-- Hello.java (*)
|
+-- HelloHome.java (*)
(*) denotes a file

9. Create the home interface.


code/Chapter6/Hello/HelloEJB/com/masslight/HelloEJBClasses/HelloHome.java
package com.masslight.HelloEJBClasses;
import java.io.Serializable;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EJBHome;
/**
This interface defines the `Home' interface for the `Hello' EJB.
*/
public interface HelloHome extends EJBHome
{
/**
Creates an instance of the `HelloBean' class on the server, and
returns a
remote reference to an hello interface on the client.
*/
Hello create() throws RemoteException, CreateException;
}

10. Create the remote interface.

111

code/Chapter6/Hello/HelloEJB/com/masslight/HelloEJBClasses/Hello.java
package com.masslight.HelloEJBClasses;
import javax.ejb.EJBObject;
import java.rmi.RemoteException;
/**
This interface defines the `Remote' interface for the `hello' EJB. Only
methods in
this interface are exposed to the outside world. The class
HelloBean implements the methods of this interface.
*/
public interface Hello extends EJBObject
{
public String sayHello(String greeting) throws RemoteException;
}

11. Create the EJB class, which contains the business logic (i.e. "real work"). In this
case, our only business logic is the sayHello method; everything else is standard
required methods that every EJB must have. Note that we don't do anything in the
methods, but they have to be there anyway.
code/Chapter6/Hello/HelloEJB/com/masslight/HelloEJBClasses/HelloBean.java
package com.masslight.HelloEJBClasses;
import java.rmi.RemoteException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
public class HelloBean implements SessionBean
{
public String sayHello(String greeting)
{
return(greeting);
}
public HelloBean() {}
public void ejbCreate() {}
public void ejbRemove() {}
public void ejbActivate() {}
public void ejbPassivate() {}
public void setSessionContext(SessionContext sc) {}
}

12. Create the deployment descriptor, which specifies that this is a stateless session
bean with the home interface, remote interface, and EJB class that we just defined
in the previous 3 files.

112

code/Chapter6/Hello/HelloEJB/META-INF/ejb-jar.xml
<?xml version="1.0" encoding="Cp1252"?>
<!DOCTYPE ejb-jar PUBLIC
'-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN'
'http://java.sun.com/dtd/ejb-jar_2_0.dtd'>
<ejb-jar>
<display-name>Hello</display-name>
<enterprise-beans>
<session>
<display-name>Hello</display-name>
<ejb-name>Hello</ejb-name>
<home>com.masslight.HelloEJBClasses.HelloHome</home>
<remote>com.masslight.HelloEJBClasses.Hello</remote>
<ejb-class>com.masslight.HelloEJBClasses.HelloBean</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Bean</transaction-type>
<security-identity>
<description></description>
<use-caller-identity></use-caller-identity>
</security-identity>
</session>
</enterprise-beans>
</ejb-jar>

13. Create the build scripts for the client code, the EJB, and the master script that ties
them all together.
code/Chapter6/Hello/HelloClient/build.xml
<project name="HelloClient" default="dist" basedir=".">
<!-- set global properties for this build -->
<property environment="env"/>
<property name="top" value="."/>
<property name="src" value="."/>
<property name="build" value="build"/>
<property name="dist" value="dist"/>
<property name="war_dir" value="${dist}/lib"/>
<property name="war_file" value="${war_dir}/HelloClient.war"/>
<property name="webinf" value="${top}/WEB-INF"/>
<property name="web.xml" value="${webinf}/web.xml"/>
<property name="classes" value="${webinf}/classes"/>
<property name="lib" value="${top}/WEB-INF/lib"/>
<property name="struts.jar" value="${env.STRUTS_HOME}/lib/struts.jar"/>
<property name="servlet.jar" value="$
{env.TOMCAT_HOME}/lib/servlet.jar"/>
<property name="ejb.src" value="${top}/../HelloEJB"/>

113

code/Chapter6/Hello/HelloClient/build.xml
<property name="ejb.jar" value="${env.JBOSS_HOME}/lib/ext/jbossj2ee.jar"/>
<property name="deploy" value="${env.JBOSS_HOME}/deploy"/>
<target name="clean">
<!-- Delete our the ${build} and ${dist} directory trees -->
<delete dir="${build}"/>
<delete dir="${dist}"/>
<delete dir="${war_dir}"/>
</target>
<target name="init">
<!-- Create the build directory structure used by compile and dist
-->
<mkdir dir="${build}"/>
<mkdir dir="${dist}"/>
<mkdir dir="${war_dir}"/>
</target>
<target name="compile" depends="init">
<!-- Compile the java code from ${src} into ${build} -->
<javac
srcdir="${ejb.src}"
destdir="${build}"
classpath="${ejb.jar}"/>
<javac
srcdir="${top}/${src}"
destdir="${build}"
classpath="${servlet.jar}:${struts.jar}:${ejb.jar}"/>
</target>
<target name="dist" depends="compile">
<!-- Put everything in a war file -->
<war warfile="${war_file}" webxml="${web.xml}">
<!-- include all JSPs in root level, and all .properties files
anywhere -->
<fileset dir="${top}/${src}">
<include name="*.jsp"/>
<include name="**/*.properties"/>
</fileset>
<!-- include all tag libraries in WEB-INF, and all .xml config
files,
but not web.xml (that's handled separately) -->
<webinf dir="${webinf}">
<include name="*.tld"/>
<include name="*.xml"/>
<exclude name="web.xml"/>
</webinf>
<!-- include all libraries in WEB-INF/lib (like struts.jar) -->
<lib dir="${lib}"/>

114

code/Chapter6/Hello/HelloClient/build.xml
<!-- include all compiled classes -->
<classes dir="${build}"/>
</war>
</target>
<target name="deploy">
<!-- Copy the war file to the JBoss deploy directory -->
<copy file="${war_file}" todir="${deploy}"/>
</target>
<target name="all" depends="clean,dist,deploy"/>
</project>

code/Chapter6/Hello/HelloEJB/build.xml
<project name="HelloEJB" default="dist" basedir=".">
<!-- set global properties for this build -->
<property environment="env"/>
<property name="top" value="."/>
<property name="src" value="."/>
<property name="build" value="build"/>
<property name="dist" value="dist"/>
<property name="jar_dir" value="${dist}/lib"/>
<property name="jar_file" value="${jar_dir}/HelloEJB.jar"/>
<property name="webinf" value="${top}/WEB-INF"/>
<property name="web.xml" value="${webinf}/web.xml"/>
<property name="classes" value="${webinf}/classes"/>
<property name="lib" value="${top}/WEB-INF/lib"/>
<property name="ejb.jar" value="${env.JBOSS_HOME}/lib/ext/jbossj2ee.jar"/>
<property name="deploy" value="${env.JBOSS_HOME}/deploy"/>
<target name="clean">
<!-- Delete our the ${build} and ${dist} directory trees -->
<delete dir="${build}"/>
<delete dir="${dist}"/>
<delete dir="${jar_dir}"/>
</target>
<target name="init">
<!-- Create the build directory structure used by compile and dist
-->
<mkdir dir="${build}"/>
<mkdir dir="${dist}"/>
<mkdir dir="${jar_dir}"/>
</target>

115

code/Chapter6/Hello/HelloEJB/build.xml
<target name="compile" depends="init">
<!-- Compile the java code from ${src} into ${build} -->
<javac
srcdir="${top}/${src}"
destdir="${build}"
classpath="${ejb.jar}"/>
</target>
<target name="dist" depends="compile">
<!-- Put everything in a jar file -->
<jar jarfile="${jar_file}">
<!-- include all compiled class files -->
<fileset dir="${build}">
<include name="**/*.class"/>
</fileset>
<!-- include ejb-jar.xml -->
<fileset dir="${top}">
<include name="META-INF/ejb-jar.xml"/>
</fileset>
</jar>
</target>
<target name="deploy">
<!-- Copy the jar file to the JBoss deploy directory -->
<copy file="${jar_file}" todir="${deploy}"/>
</target>
<target name="all" depends="clean,dist,deploy"/>
</project>

code/Chapter6/Hello/build.xml
<project name="Hello" default="all" basedir=".">
<target name="all">
<ant dir="HelloClient" target="all"/>
<ant dir="HelloEJB" target="all"/>
</target>
<target name="clean">
<ant dir="HelloClient" target="clean"/>
<ant dir="HelloEJB" target="clean"/>
</target>
</project>

116
14. From the root Hello directory, do ant all to compile both the client code (in
HelloClient) and the EJB code (in HelloEJB) and deploy both automatically.
This "master" build script will call the individual build scripts we created for the
client code and the EJB code. This is one of the most powerful features of Ant:
being able to automatically tie together subprojects (which you can build and test
separately) to build and deploy everything all at once.
15. Go to http://localhost:8080/HelloClient/getEJB.do to test your
application. This will be the most complicated "Hello World" program you will
ever build.

StatefulHello: A Stateful Session Bean


A stateful session bean is like a stateless session bean, except that it includes accessor and
mutator methods for its properties. Its implementation is very similar; the most important
difference to remember is to declare the bean as Stateful in the ejb-jar.xml descriptor
file.

Do this:
1. Duplicate the entire Hello directory from the previous example. Rename it to
StatefulHello. Your directory structure should look very familiar:
code/Chapter6/StatefulHello/HelloClient
HelloClient
|
+-- build.xml (*)
|
+-- index.jsp (*)
|
+-- WEB-INF
|
+-- web.xml (*)
|
+-- app.tld (*)
|
+-- struts-bean.tld (*)
|
+-- struts-form.tld (*)
|

117

code/Chapter6/StatefulHello/HelloClient
+-|
+-|
+-|
+-|
+-|
+-|
|
|
|
|
|
|
|
|
|
|
|
|
+--

struts-html.tld (*)
struts-logic.tld (*)
struts-template.tld (*)
struts.tld (*)
struts-config.xml (*)
classes
|
+-- com
|
+-- masslight
|
+-- beans
|
|
|
+-- HelloClientBean.java (*)
|
+-- actions
|
+-- GetBeanAction.java (*)
lib
|
+-- struts.jar (*)

(*) denotes a file

2. In the client code, everything is the same except two files: the Action class which
calls the client bean, and the client bean that calls the EJB. Create the Action
class.
code/Chapter6/StatefulHello/HelloClient/WEBINF/classes/com/masslight/actions/GetBeanAction.java
package com.masslight.actions;
import
import
import
import
import
import
import
import
import

java.util.Vector;
java.io.IOException;
java.lang.reflect.InvocationTargetException;
java.util.Locale;
java.util.Hashtable;
javax.servlet.*;
javax.servlet.http.*;
org.apache.struts.action.*;
org.apache.struts.util.*;

118

code/Chapter6/StatefulHello/HelloClient/WEBINF/classes/com/masslight/actions/GetBeanAction.java
import com.masslight.beans.HelloClientBean;
public final class GetBeanAction extends Action {
// The constructor method for this class
public GetBeanAction() {
}
// This sets the list as a session bean
public ActionForward perform(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
HttpSession session = request.getSession();
HelloClientBean clientbean = new HelloClientBean();
clientbean.startup();
session.setAttribute("greeting", clientbean.sayGreeting("Hello
from a stateful EJB!"));
return (mapping.findForward("success"));

}
}

3. Create the client bean.


code/Chapter6/Hello/HelloClient/WEBINF/classes/com/masslight/beans/HelloClientBean.java
package com.masslight.beans;
import
import
import
import

javax.naming.*;
java.util.Hashtable;
javax.rmi.PortableRemoteObject;
javax.ejb.*;

public class HelloClientBean


{
com.masslight.HelloEJBClasses.Hello hello = null;
com.masslight.HelloEJBClasses.HelloHome home = null;
public HelloClientBean() {
}
/**

119

code/Chapter6/Hello/HelloClient/WEBINF/classes/com/masslight/beans/HelloClientBean.java
This method does all the work. It creates an instance of the
hello EJB on
the EJB server, and calls its `sayHello()' method, then prints
the result the returned String.
*/
public void startup () {
// Set up the naming provider; this may not always be necessary,
depending
// on how your Java system is configured.
System.setProperty("java.naming.factory.initial",
"org.jnp.interfaces.NamingContextFactory");
System.setProperty("java.naming.provider.url",
"localhost:1099");
// A single `try' block is not an ideal way to do exception
handling, but it
// is easier to read code without all the catch blocks
try
{
// Get a naming context
InitialContext jndiContext = new InitialContext();
System.out.println("Got context \n");
// Get a reference to the hello Bean
Object ref = jndiContext.lookup("Hello");
System.out.println("Got reference \n");
// Get a reference from this to the Bean's Home interface
home = (com.masslight.HelloEJBClasses.HelloHome)
PortableRemoteObject.narrow (ref,
com.masslight.HelloEJBClasses.HelloHome.class);
// Create an hello object from the Home interface
hello = home.create();
}
{
}

catch(Exception e)
System.out.println(e.toString());

}
public String sayGreeting(String greeting) {
String output = new String("");
try {
output += hello.sayHello(greeting);
} catch(Exception e)
{
System.out.println(e.toString());
}
return(output);
}

120
4. You should already have the directory structure for the EJB, since you duplicated
it along with the client code.
code/Chapter6/StatefulHello/HelloEJB
HelloEJB
|
+-- build.xml (*)
|
+-- META-INF
|
|
|
+-- ejb-jar.xml (*)
|
+-- com
|
+-- masslight
|
+-- HelloEJBClasses
|
+-- HelloBean.java (*)
|
+-- Hello.java (*)
|
+-- HelloHome.java (*)
(*) denotes a file

5. The home interface is the same, but all other files need additional changes to
accomodate the accessor and mutator methods and the statefulness of this EJB.
code/Chapter6/StatefulHello/HelloEJB/com/masslight/HelloEJBClasses/HelloHome.java
package com.masslight.HelloEJBClasses;
import java.io.Serializable;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EJBHome;
/**
This interface defines the `Home' interface for the `Hello' EJB.
*/
public interface HelloHome extends EJBHome
{
/**
Creates an instance of the `HelloBean' class on the server, and returns
a
remote reference to an hello interface on the client.
*/
Hello create() throws RemoteException, CreateException;

121

code/Chapter6/StatefulHello/HelloEJB/com/masslight/HelloEJBClasses/HelloHome.java
}

6. Create the remote interface, which has additional setGreeting and getGreeting
methods (compared with the stateless version in the previous example).
code/Chapter6/StatefulHello/HelloEJB/com/masslight/HelloEJBClasses/Hello.java
package com.masslight.HelloEJBClasses;
import javax.ejb.EJBObject;
import java.rmi.RemoteException;
public interface Hello extends EJBObject
{
public void setGreeting(String newGreeting) throws RemoteException;
public String getGreeting() throws RemoteException;
public String sayHello() throws RemoteException;
}

7. Create the EJB class, which has additional getGreeting and setGreeting code,
as well as an instance variable greeting. This is the state that this stateful EJB
holds.
code/Chapter6/StatefulHello/HelloEJB/com/masslight/HelloEJBClasses/HelloBean.java
package com.masslight.HelloEJBClasses;
import java.rmi.RemoteException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
public class HelloBean implements SessionBean
{
String greeting;
public void setGreeting(String newGreeting) {
greeting = newGreeting;
}
public String getGreeting() {
return (greeting);
}
public String sayHello()
{
return(getGreeting());
}
public HelloBean() {}
public void ejbCreate() {}

122

code/Chapter6/StatefulHello/HelloEJB/com/masslight/HelloEJBClasses/HelloBean.java
public
public
public
public

void
void
void
void

ejbRemove() {}
ejbActivate() {}
ejbPassivate() {}
setSessionContext(SessionContext sc) {}

8. Create the ejb.jar.xml descriptor file, which specifies that this EJB is stateful,
and that it has the home interface, remote interface, and EJB class that we just
defined in the previous 3 files.
code/Chapter6/StatefulHello/HelloEJB/META-INF/ejb-jar.xml
<?xml version="1.0" encoding="Cp1252"?>
<!DOCTYPE ejb-jar PUBLIC
'-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN'
'http://java.sun.com/dtd/ejb-jar_2_0.dtd'>
<ejb-jar>
<display-name>Hello</display-name>
<enterprise-beans>
<session>
<display-name>Hello</display-name>
<ejb-name>Hello</ejb-name>
<home>com.masslight.HelloEJBClasses.HelloHome</home>
<remote>com.masslight.HelloEJBClasses.Hello</remote>
<ejb-class>com.masslight.HelloEJBClasses.HelloBean</ejb-class>
<session-type>Stateful</session-type>
<transaction-type>Bean</transaction-type>
<security-identity>
<description></description>
<use-caller-identity></use-caller-identity>
</security-identity>
</session>
</enterprise-beans>
</ejb-jar>

9. Create the build scripts for the client code, the EJB code, and the master script
that ties them all together.
code/Chapter6/StatefulHello/HelloClient/build.xml
<project name="HelloClient" default="dist" basedir=".">
<!-- set global properties for this build -->

123

code/Chapter6/StatefulHello/HelloClient/build.xml
<property
<property
<property
<property
<property
<property
<property

environment="env"/>
name="top" value="."/>
name="src" value="."/>
name="build" value="build"/>
name="dist" value="dist"/>
name="war_dir" value="${dist}/lib"/>
name="war_file" value="${war_dir}/HelloClient.war"/>

<property name="webinf" value="${top}/WEB-INF"/>


<property name="web.xml" value="${webinf}/web.xml"/>
<property name="classes" value="${webinf}/classes"/>
<property name="lib" value="${top}/WEB-INF/lib"/>
<property name="struts.jar" value="${env.STRUTS_HOME}/lib/struts.jar"/>
<property name="servlet.jar" value="$
{env.TOMCAT_HOME}/lib/servlet.jar"/>
<property name="ejb.src" value="${top}/../HelloEJB"/>
<property name="ejb.jar" value="${env.JBOSS_HOME}/lib/ext/jbossj2ee.jar"/>
<property name="deploy" value="${env.JBOSS_HOME}/deploy"/>
<target name="clean">
<!-- Delete our the ${build} and ${dist} directory trees -->
<delete dir="${build}"/>
<delete dir="${dist}"/>
<delete dir="${war_dir}"/>
</target>
<target name="init">
<!-- Create the build directory structure used by compile and dist
-->
<mkdir dir="${build}"/>
<mkdir dir="${dist}"/>
<mkdir dir="${war_dir}"/>
</target>
<target name="compile" depends="init">
<!-- Compile the java code from ${src} into ${build} -->
<javac
srcdir="${ejb.src}"
destdir="${build}"
classpath="${ejb.jar}"/>
<javac
srcdir="${top}/${src}"
destdir="${build}"
classpath="${servlet.jar}:${struts.jar}:${ejb.jar}"/>
</target>
<target name="dist" depends="compile">
<!-- Put everything in a war file -->
<war warfile="${war_file}" webxml="${web.xml}">
<!-- include all JSPs in root level, and all .properties files
anywhere -->
<fileset dir="${top}/${src}">

124

code/Chapter6/StatefulHello/HelloClient/build.xml
<include name="*.jsp"/>
<include name="**/*.properties"/>
</fileset>
<!-- include all tag libraries in WEB-INF, and all .xml config
files,
but not web.xml (that's handled separately) -->
<webinf dir="${webinf}">
<include name="*.tld"/>
<include name="*.xml"/>
<exclude name="web.xml"/>
</webinf>
<!-- include all libraries in WEB-INF/lib (like struts.jar) -->
<lib dir="${lib}"/>
<!-- include all compiled classes -->
<classes dir="${build}"/>
</war>
</target>
<target name="deploy">
<!-- Copy the war file to the JBoss deploy directory -->
<copy file="${war_file}" todir="${deploy}"/>
</target>
<target name="all" depends="clean,dist,deploy"/>
</project>

code/Chapter6/StatefulHello/HelloEJB/build.xml
<project name="HelloEJB" default="dist" basedir=".">
<!-- set global properties for this build -->
<property environment="env"/>
<property name="top" value="."/>
<property name="src" value="."/>
<property name="build" value="build"/>
<property name="dist" value="dist"/>
<property name="jar_dir" value="${dist}/lib"/>
<property name="jar_file" value="${jar_dir}/HelloEJB.jar"/>
<property name="webinf" value="${top}/WEB-INF"/>
<property name="web.xml" value="${webinf}/web.xml"/>
<property name="classes" value="${webinf}/classes"/>
<property name="lib" value="${top}/WEB-INF/lib"/>
<property name="ejb.jar" value="${env.JBOSS_HOME}/lib/ext/jbossj2ee.jar"/>

125

code/Chapter6/StatefulHello/HelloEJB/build.xml
<property name="deploy" value="${env.JBOSS_HOME}/deploy"/>
<target name="clean">
<!-- Delete our the ${build} and ${dist} directory trees -->
<delete dir="${build}"/>
<delete dir="${dist}"/>
<delete dir="${jar_dir}"/>
</target>
<target name="init">
<!-- Create the build directory structure used by compile and dist
-->
<mkdir dir="${build}"/>
<mkdir dir="${dist}"/>
<mkdir dir="${jar_dir}"/>
</target>
<target name="compile" depends="init">
<!-- Compile the java code from ${src} into ${build} -->
<javac
srcdir="${top}/${src}"
destdir="${build}"
classpath="${ejb.jar}"/>
</target>
<target name="dist" depends="compile">
<!-- Put everything in a jar file -->
<jar jarfile="${jar_file}">
<!-- include all compiled class files -->
<fileset dir="${build}">
<include name="**/*.class"/>
</fileset>
<!-- include ejb-jar.xml -->
<fileset dir="${top}">
<include name="META-INF/ejb-jar.xml"/>
</fileset>
</jar>
</target>
<target name="deploy">
<!-- Copy the jar file to the JBoss deploy directory -->
<copy file="${jar_file}" todir="${deploy}"/>
</target>
<target name="all" depends="clean,dist,deploy"/>
</project>

126

code/Chapter6/StatefulHello/build.xml
<project name="StatefulHello" default="all" basedir=".">
<target name="all">
<ant dir="HelloClient" target="all"/>
<ant dir="HelloEJB" target="all"/>
</target>
<target name="clean">
<ant dir="HelloClient" target="clean"/>
<ant dir="HelloEJB" target="clean"/>
</target>
</project>

10. ant all from the root StatefulHello directory to compile and deploy both the
client and EJB code.
11. Go to http://localhost:8080/HelloClient/getEJB.do to test this
application.

Entity Beans
Container-managed versus Bean-managed
An entity EJB is either bean-managed or container-managed. In a container-managed
entity bean, the container handles the beans transactions with a database. In a beanmanaged EJB, the bean class must handle the interactions with the database. The
deployment descriptor for a container-managed bean includes information about the
beans methods that a similar bean-managed EJB's deployment descriptor would not.
Currently, container-managed beans are not well supported because EJB containers are
not yet completely uniform, in regards to deployment of container-managed beans, and
because not all containers manage container-managed beans very well. This means that
bean-managed entity beans are the current de facto implementation of entity beans.
Implementing Entity Beans
Entity beans require a home interface, a remote interface, a bean class, and an XML
deployment descriptor. All four parts need to follow certain guidelines in order to be
correctly deployed by the EJB container. The home interface extends the
javax.ejb.EJBHome interface and the remote interface extends the
javax.ejb.EJBObject interface. The bean class needs to implement the
javax.ejb.EntityBean interface.
Since an entity bean is meant to model a table in a database, it must have properties that
correspond to the columns of the table. Below is an example of a table in a database and
the corresponding properties of the table in a bean class.

127

SQL for example table created

corresponding properties declared in the bean


class

CREATE TABLE DEMO (

// no corresponding property

ID integer(8) not null,

private long id;

COLUMN_ONE varchar(32) not


null,

private String columnOne;

primary key(ID));

// no corresponding property

The bean class must implement all the methods declared in the home, remote, and
javax.ejb.EntityBean interfaces. Some of the methods that the bean class must
implement do not have the exact same names as the methods that are declared in the
interface. For example, create( arguments ) in the home interface is ejbCreate(
same_arguments )in the bean class. Below is an outline of the methods required in the
remote interface, the home interface, and the bean class of an entity bean. The EJB
specification defines javax.ejb.EntityBean interface as the following, along with its
superclass javax.ejb.EnterpriseBean:
interface EnterpriseBean
public interface EnterpriseBean extends java.io.Serializable {}
interface EntityBean
public interface EntityBean extends EnterpriseBean {
public abstract void ejbActivate() throws java.rmi.RemoteException;
public abstract void ejbLoad() throws java.rmi.RemoteException;
public abstract void ejbPassivate() throws java.rmi.RemoteException;
public abstract void ejbRemove() throws java.rmi.RemoteException;
public abstract void ejbStore() throws java.rmi.RemoteException;
public abstract void setEntityContext(EntityContext ctx) throws
java.rmi.RemoteException;
public abstract void unsetEntityContext() throws
java.rmi.RemoteException;
}

Below are tables detailing the necessary elements of the remote interface, the home
interface, and the bean class of a bean-managed or container-managed entity bean.
remote interface extends javax.ejb.EJBObject
method name
exampleMethod(int
GNPofFranceInBillionsOfPounds;

returns description
int
All methods declared in this interface are
specific to a particular EJB. Each method
declared here must also be implemented in
the bean class. Note: this is an example
method only used to show that methods
declared in the remote interface must be

128
implemented in the bean class. Not
Required.
home interface extends javax.ejb.EJBHome

method name

Returns

create( ... ) throws


java.rmi.RemoteException,
javax.ejb.CreateException

an instance of a
stub associated
with the remote
interface of an
instance of the
bean class

findByPrimaryKey
(primary_key_type
primary_key) throws
java.rmi.RemoteException,
javax.ejb.FinderException

an instance of a
stub associated
with the remote
interface of the
instance of the
bean class with
the primary key

description
This should create a new row in the
table - provided that a row with that
primary key does not exist already.
When the container receives this
call, it creates an instance of the
entity bean. Tthe create method
can include parameters, as long as
each create method declaration
matches the implementation of an
ejbCreate method in the bean
class. There must be at least one
create method, but there can be
more than one. Required.
This should return the row in the
table with the primary key
primary_key. It should also have a
method in the bean called
ejbFindByPrimaryKey. Required.

primary_key

This method should remove the


current row from the table.
public void remove() throws
FinderException,
void
The declaration should correspond
RemoteException
to the method in the bean called
ejbRemove. Not required.
a Collection of Should return all the primary keys
findAll() or similar name
in a table - does not need to be
all the primary
keys in the table called findAll. Not required.
bean class implements javax.ejb.EntityBean

method name
ejbCreate( ... ) throws
javax.ejb.CreateException

returns

description
primary_key_type This should add a row
to the table in the
database and set the
properties of the bean
to the values of the
attributes of the row.

129
There must be at least
one ejbCreate method
in the bean class, but
there can be more than
one. Required.
Alerts the bean class
that the container has
ejbPostCreate( ... )
void
finished the call to an
ejbCreate method.
Required.
This should return the
row with the primary
key primary_key if
ejbFindByPrimaryKey(primary_key_type
primary_key) throws
primary_key_type that row exists,
otherwise it should
javax.ejb.FinderException
throw a
FinderException.

ejbFindAll()

Collection

ejbLoad()

void

ejbStore()

void

ejbRemove()

void

Required.
This method should
return all the primary
keys in a table. It
should correspond to
the findAll method in
the home interface, if
one exists. Not
required.
This method should
load into the EJB a row
in the table whose
primary key is the
value of the primary
key attribute in the
bean. Required.
This method should
update the contents of a
row in the database
whose primary key is
the value of the
primary key attribute in
the bean. Required.
The container calls this
method before it
removes the bean from
itself. This method
should remove the row

130

ejbActivate()

void

ejbPassivate()

void

setEntityContext(EntityContext ctx)

void

unsetEntityContext()

void

exampleMethod(int
GNPofFranceInBillionsOfPounds)

int

Important interfaces and classes of the javax.ejb package


javax.ejb.EJBHome
The home interface extends
interface EJBHome

the EJBHome interface.

in the table that


corresponds with the
state of the bean.
Required.
The container calls this
method after it brings
the EJB back from
secondary memory.
Required.
The container calls this
method before it moves
the EJB to secondary
memory. Required.
The container calls this
method after the bean
has been created but
before any of the
ejbCreate methods
have been called. The
bean is responsible for
storing the entity
context object.
Required.
The container calls this
method before it
destroys the EJB.
Required.
This method would
need to be in the bean
class if it is declared in
the remote interface.
Note: this is an
example method
demonstrating the need
to implement methods
declared in the remote
interface. Not required.

131
public interface EJBHome extends java.rmi.Remote {
public abstract EJBMetaData getEJBMetaData() throws
java.rmi.RemoteException;
public abstract void remove(Object primaryKey) throws
java.rmi.RemoteException, RemoveException;
public abstract void remove(Handle handle) throws
java.rmi.RemoteException, RemoveException;
}
interface javax.ejb.EJBHome
method declaration

description

public abstract EJBMetaData


getEJBMetaData() throws
java.rmi.RemoteException;

will return the EJBMetaData


object, which can be used for
introspection by EJB developer
tools

public abstract void remove(Object


primaryKey) throws
java.rmi.RemoteException, RemoveException;

will remove the client's reference to


the EJB in the container

public abstract void remove(Handle handle)


throws java.rmi.RemoteException,
RemoveException;

will remove the client's reference to


the EJB in the container

javax.ejb.EJBObject

The remote interface extends the EJBObject interface.


interface EJBObject
public interface EJBObject extends java.rmi.Remote {
public abstract EJBHome getEJBHome() throws java.rmi.RemoteException;
public abstract Handle getHandle() throws java.rmi.RemoteException;
public abstract void getPrimaryKey() throws java.rmi.RemoteException;
public abstract boolean isIdentical(EJBObject obj) throws
java.rmi.RemoteException;
public abstract void remove() throws java.rmi.RemoteException,
RemoveException;
}
interface javax.ejb.EJBObject

method declaration

description

public abstract EJBHome getEJBHome() throws


java.rmi.RemoteException;

will return a stub to the


corresponding home interface

public abstract Handle getHandle() throws


java.rmi.RemoteException;

will return a handle to the


remote interface

public abstract void getPrimaryKey() throws


java.rmi.RemoteException;

will return the primary key of


EJB, if the EJB is an entity bean

132
public abstract boolean isIdentical(EJBObject
obj) throws java.rmi.RemoteException;

will compare two remote stubs


to see if they are the same

public abstract void remove() throws


java.rmi.RemoteException, RemoveException;

will remove client's reference to


the remote interface

javax.ejb.EJBContext
interface EJBContext
public interface EJBContext extends java.rmi.Remote {
public abstract java.security.Identity getCallerIdentity();
public abstract EJBHome getEJBHome();
public abstract java.util.Properties getEnvironment();
public abstract boolean getRollbackOnly();
public abstract javax.jts.UserTransaction getUserTransaction() throws
java.lang.IllegalStateException;
public abstract boolean IsCallerInRole(java.security.identity role);
public abstract void setRollbackOnly();
}
interface javax.ejb.EJBContext

method declaration

description

public abstract java.security.Identity


getCallerIdentity();

will allow the caller of the method to learn


the identity of the caller of the EJB

public abstract EJBHome getEJBHome();

will return a handle to the remote interface

public abstract java.util.Properties


getEnvironment();

will return a list of properties that the EJB


container sends to its objects

public abstract boolean


getRollbackOnly();

will tell the EJB object if it is executing


within a transaction that has been rolled
back

public abstract
javax.jts.UserTransaction
getUserTransaction() throws
java.lang.IllegalStateException;

will return the container's implementation


of the

public abstract boolean


IsCallerInRole(java.security.identity
role);

will check to see if the caller of the EJB is


within the caller's proper security role

public abstract void


setRollbackOnly();

sets the current transaction, if there is a


transaction, to be rolled back

javax.ejb.Handle
interface Handle
public interface Handle{

javax.transaction.UserTransaction

interface

133
public abstract EJBObject getEJBObject() throws
java.rmi.RemoteException;
}
interface javax.ejb.Handle

public abstract EJBObject getEJBObject()


throws java.rmi.RemoteException;

will return a remote reference to


the EJBObject that it is
representing

javax.ejb.EntityContext
interface EntityContext
public interface EntityContext extends javax.ejb.EJBContext{
public abstract EJBObject getEJBObject() throws
java.lang.IllegalStateException;
public abstract Object getPrimaryKey() throws
java.lang.IllegalStateException;
}
interface javax.ejb.EntityContext

method declaration

description

public abstract EJBObject getEJBObject() throws


java.lang.IllegalStateException;

will return the stub for the


remote interface

public abstract Object getPrimaryKey() throws


java.lang.IllegalStateException;

will return the primary key


of the row associated with
the EJB

javax.ejb.SessionContext
interface SessionContext
public interface SessionContext extends javax.ejb.EJBContext {
public abstract EJBObject getEJBObject() throws
java.lang.IllegalStateException;
}
interface javax.ejb.SessionContext

method declaration

description

public abstract EJBObject getEJBObject() throws


java.lang.IllegalStateException;

will return the stub for


the remote interface

javax.ejb.EJBMetaData

134
This interface can be used for introspection by an EJB development tool
interface EJBMetaData
public
public
public
public
public
public
}

interface EJBMetaData {
abstract EJBHome getEJBHome();
abstract Class getHomeInterfaceClass();
abstract Class getPrimaryKeyClass();
abstract Class getRemoteInterfaceClass();
abstract boolean isSession();

interface javax.ejb.EJBMetaData

method declaration

description

public abstract EJBHome getEJBHome();

will return the stub for the home


interface

public abstract Class


getHomeInterfaceClass();

will return the home interface class

public abstract Class


getPrimaryKeyClass();

will return the primary key class, if the


EJB is an entity bean

public abstract Class


getRemoteInterfaceClass();

will return the remote interface class

public abstract boolean isSession();

will return whether the EJB is a session


bean or not

Accessing Databases
JDBC is the industry standard way to access databases from Java applications. There is a
JDBC driver for virtually every database you can imagine; many databases have more
than one available, which you can choose between based on the needs of your
application.
Configuring JBoss to use an OpenBase database
First, add the Openbase JDBC driver (OpenBase.jar) to the lib/ext directory under
JBoss.
Second, we must tell JBoss some more information about this driver. Open the
jboss.jcml configuration file and find the JDBC configuration section. Add the class for
the OpenBase JDBC driver (com.openbase.jdbc.ObDriver) to the list of classes in the
JDBC section:

<mbean code="org.jboss.jdbc.JdbcProvider"

135

name="DefaultDomain:service=JdbcProvider">
<attribute
name="Drivers">org.hsqldb.jdbcDriver,com.openbase.jdbc.ObDriver</attribute>
</mbean>

Coding with Databases


Accessing a database consists of four main steps.
1.
2.
3.
4.

Create a database connection, using the appropriate JDBC driver


Create a SQL statement
Send the SQL statement to the database to select or update data
Close the database connection when it is no longer needed

Loading a JDBC Driver

String driverClass = "com.openbase.jdbc.ObDriver";


Class.forName(driverClass);

Connecting To The Database

try {
String database = new String("jdbc:openbase://127.0.0.1/phone");
String username = new String("admin");
String password = new String("");
java.sql.Connection connection =
DriverManager.getConnection(database, username, password);
} catch (SQLException e) {
System.out.println("AN SQL Exception occurred while getting a
connection");
}

Fetching Data

String sqlString = "select FIRST_NAME, LAST_NAME, PHONE_NUMBER " +


"from PHONEPAGES where PHONE_ID=";
sqlString += $PHONE_ID_TO_FETCH$;
Statement s = connection.createStatement();
s.executeQuery(sqlString);

Accessing Fetched Data

136

ResultSet rs = s.getResultSet();
if (rs.next()) {
this.firstName = rs.getString(1);
this.lastName = rs.getString(2);
this.phoneNumber = rs.getString(3);
}

Updating Data

try {
String sqlString = "update PHONEPAGES set FIRST_NAME = '";
sqlString += tempFirstName;
sqlString += "'" + ", LAST_NAME = '";
sqlString += tempLastName;
sqlString += "'" + ", PHONE_NUMBER = '";
sqlString += tempPhoneNumber;
sqlString += "'";
sqlString += " where PHONE_ID = ";
sqlString += tempPhoneNumber;
connection.setAutoCommit(false);
Statement s = connection.createStatement();
s.executeUpdate(sqlString);
connection.commit();
s.close();
} catch (SQLException e) {
connection.rollback();
}

Inserting New Data

try {
String sqlString = "insert into PHONEPAGES " +
"(PHONE_ID, FIRST_NAME, LAST_NAME, PHONE_NUMBER) values (";
sqlString += tempPhoneId;
sqlString += ", '" + tempFirstName;
sqlString += "', '" + tempLastName;
sqlString += "', '" + tempPhoneNumber;
sqlString += "')";
connection.setAutoCommit(false);
Statement s = connection.createStatement();
s.executeUpdate(sqlString);
connection.commit();
s.close();
} catch (SQLException e) {
connection.rollback();
}

137
Deleting Data

try {
String sqlString = "delete from PHONEPAGES where PHONE_ID = ";
sqlString += tempPhoneId;
connection.setAutoCommit(false);
Statement s = connection.createStatement();
s.executeUpdate(sqlString);
connection.commit();
s.close();
} catch (SQLException e) {
connection.rollback();
}

Closing The Connection

try {
connection.close();
} catch (SQLException e) {
System.out.println("An SQL Exception occurred while closing the
connection");
}

Phone: A Bean-managed Entity Bean


Do this:
1. Create the directory structure. The root directory should be Phone. Within that, we
will have two separate directories, PhoneClient and PhoneEJB. PhoneClient
looks just like the rest of the web applications we've been building, including
Hello and StatefulHello. It uses the Struts framework and has the directory
hierarchies that should look very familiar by now.
2. Copy the Struts framework into WEB-INF.
code/Chapter6/Phone/PhoneClient
PhoneClient
|
+-- index.jsp (*)
|

138

code/Chapter6/Phone/PhoneClient
+-- build.xml (*)
|
+-- WEB-INF
|
+-- 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
|
|
|
+-- beans
|
|
|
|
|
+-- PhoneClientBean.java (*)
|
|
|
+-- actions
|
|
|
+-- GetBeanAction.java (*)
|
+-- lib
|
+-- struts.jar (*)
(*) denotes a file

3. Create the JSP, Action class, and client bean.


code/Chapter6/Phone/PhoneClient/index.jsp
<%@ page language="java" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>

139

code/Chapter6/Phone/PhoneClient/index.jsp
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<html:html>
<head>
<title>
Entity Bean
</title>
<html:base/>
</head>
<body>
<logic:present name="greeting">
<bean:write name="greeting"/>
</logic:present>
<br/>
<logic:present name="addRow">
<bean:write name="addRow"/>
</logic:present>
<br/>
<logic:present name="fetchRow">
<bean:write name="fetchRow"/>
</logic:present>
<br/>
<logic:present name="printRow">
<bean:write name="printRow"/>
</logic:present>
<br/>
</body>
</html:html>

code/Chapter6/Phone/PhoneClient/WEBINF/classes/com/masslight/actions/GetBeanAction.java
package com.masslight.actions;
import java.util.Vector;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Locale;
import java.util.Hashtable;
import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.struts.action.*;
import org.apache.struts.util.*;
import com.masslight.beans.PhoneClientBean;
public final class GetBeanAction extends Action {

140

code/Chapter6/Phone/PhoneClient/WEBINF/classes/com/masslight/actions/GetBeanAction.java
// The constructor method for this class
public GetBeanAction() {
}
// this is called when the .../getEJB.do URL is requested from the
browser
public ActionForward perform(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
HttpSession session = request.getSession();
PhoneClientBean clientbean = new PhoneClientBean();
session.setAttribute("greeting", clientbean.startup("Hello"));
session.setAttribute("addRow",
clientbean.addRow(new Integer("440"), "Joe", "Orange",
"1231112222"));
session.setAttribute("fetchRow", clientbean.fetchRow(new
Integer("440")));
session.setAttribute("printRow", clientbean.printRow());
}

return (mapping.findForward("success"));

code/Chapter6/Phone/PhoneClient/WEBINF/classes/com/masslight/beans/PhoneClientBean.java
package com.masslight.beans;
import
import
import
import
import

com.masslight.PhoneNumberEJBClasses.*;
javax.naming.*;
java.util.Hashtable;
javax.rmi.PortableRemoteObject;
javax.ejb.*;

public class PhoneClientBean


{

141

code/Chapter6/Phone/PhoneClient/WEBINF/classes/com/masslight/beans/PhoneClientBean.java
String jndi_name = new String("Phone");
private PhoneNumber phone = null;
private PhoneNumberHome home = null;
public String startup (String greeting) {
System.setProperty("java.naming.factory.initial",
"org.jnp.interfaces.NamingContextFactory");
System.setProperty("java.naming.provider.url",
"localhost:1099");
String output = new String("");
try
{
// Get a naming context
InitialContext jndiContext = new InitialContext();
System.out.println("Got context \n");
// Get a reference to the PhoneNumber Bean
Object ref = jndiContext.lookup(jndi_name);
System.out.println("Got reference \n");
// Get a reference from this to the Bean's Home interface
this.home = ( PhoneNumberHome)
PortableRemoteObject.narrow (ref,
PhoneNumberHome.class);
System.out.println("Got home \n");
}
catch(Exception e)
{
System.out.println(e.toString());
}
return(greeting);
}
public String addRow(Integer phoneId, String firstName,
String lastName, String phoneNumber) {
try {
this.phone = this.home.create(phoneId, firstName, lastName,
phoneNumber);
}
catch (Exception e) {
e.printStackTrace();
return ("error - row could not be added");
}
return ("row added");
}

142

code/Chapter6/Phone/PhoneClient/WEBINF/classes/com/masslight/beans/PhoneClientBean.java
public String fetchRow(Integer tempPhoneId) {
try {
this.phone = this.home.findByPrimaryKey(tempPhoneId);
}
catch (Exception e) {
System.out.println("Error in fetching row");
e.printStackTrace();
return ("error - row could not be fetched");
}
return ("row fetched");
}
public String printRow () {
try {
String printString = new String("");
printString += this.phone.getPhoneId() + ", " +
this.phone.getFirstName() + ", " +
this.phone.getLastName() + ", " +
this.phone.getPhoneNumber();
return (printString);

}
catch (Exception e) {
System.out.println("Error in printing row");
e.printStackTrace();
return ("error - row could not be printed");
}

4. Create the EJB directory structure.


code/Chapter6/Phone/PhoneEJB
PhoneEJB
|
+-- build.xml (*)

143

code/Chapter6/Phone/PhoneEJB
|
+-- META-INF
|
|
|
+-- ejb-jar.xml (*)
|
+-- com
|
+-- masslight
|
+-- PhoneNumberEJBClasses
|
+-- PhoneNumber.java (*)
|
+-- PhoneNumberHome.java (*)
|
+-- PhoneNumberBean.java (*)
(*) denotes a file

5. Create the home interface, remote interface, and EJB bean classes.
code/Chapter6/Phone/PhoneEJB/com/masslight/PhoneNumberEJBClasses/PhoneNumberHome.java
package com.masslight.PhoneNumberEJBClasses;
import java.util.Collection;
import java.rmi.RemoteException;
import javax.ejb.*;
public interface PhoneNumberHome extends EJBHome {
public PhoneNumber create(Integer phoneId,
String firstName,
String lastName,
String phoneNumber)
throws RemoteException, CreateException;

public PhoneNumber findByPrimaryKey(Integer phoneId)


throws FinderException, RemoteException;

code/Chapter6/Phone/PhoneEJB/com/masslight/PhoneNumberEJBClasses/PhoneNumber.java
package com.masslight.PhoneNumberEJBClasses;
import javax.ejb.EJBObject;
import java.rmi.RemoteException;

144

code/Chapter6/Phone/PhoneEJB/com/masslight/PhoneNumberEJBClasses/PhoneNumber.java
public interface PhoneNumber extends EJBObject {
public Integer getPhoneId() throws RemoteException;
public String getFirstName() throws RemoteException;
public String getLastName() throws RemoteException;
public String getPhoneNumber() throws RemoteException;
}

code/Chapter6/Phone/PhoneEJB/com/masslight/PhoneNumberEJBClasses/PhoneNumberBean.j
ava
package com.masslight.PhoneNumberEJBClasses;
import java.sql.*;
import java.util.Enumeration;
import java.util.Vector;
import java.util.Collection;
import java.util.Iterator;
import javax.ejb.*;
public class PhoneNumberBean implements EntityBean {
private EntityContext context = null;
private Connection connection = null;
private
private
private
private

Integer phoneId = new Integer(0);


String firstName = new String();
String lastName = new String();
String phoneNumber = new String();

/////////////////////////////////////////////////////////////////////////////
public void makeConnection() {
System.out.println("in makeConnection");
try {
Class.forName("com.openbase.jdbc.ObDriver");
try {
String database = new String("jdbc:openbase://127.0.0.1/phone");
String username = new String("admin");
String password = new String("");
connection = DriverManager.getConnection(database, username,
password);
} catch (SQLException e) {
System.out.println("AN SQL Exception occurred while getting a
connection");
}
} catch (ClassNotFoundException cnfe) {
System.out.println("Failed to load JDBC drivers.");
}
System.out.println("leaving makeConnection");
}

145

code/Chapter6/Phone/PhoneEJB/com/masslight/PhoneNumberEJBClasses/PhoneNumberBean.j
ava
/////////////////////////////////////////////////////////////////////////////
public void closeConnection() {
System.out.println("in closeConnection");
try {
connection.close();
} catch (SQLException e) {
System.out.println("An SQL Exception occurred while closing the
connection");
}
System.out.println("leaving closeConnection");
}
/////////////////////////////////////////////////////////////////////////////
public Integer ejbCreate(Integer tempPhoneId,
String tempFirstName,
String tempLastName,
String tempPhoneNumber) throws CreateException {
System.out.println("in ejbCreate");
try {
insertRow(tempPhoneId, tempFirstName, tempLastName, tempPhoneNumber);
} catch (Exception ex) {
throw new EJBException("ejbCreate: " + ex.getMessage());
}
this.phoneId = tempPhoneId;
this.firstName = tempFirstName;
this.lastName = tempLastName;
this.phoneNumber = tempPhoneNumber;
System.out.println("leaving ejbCreate with " + tempPhoneId);
return tempPhoneId;
}
public void ejbPostCreate(Integer tempPhoneId,
String tempFirstName,
String tempLastName,
String tempPhoneNumber) {
System.out.println("in ejbPostCreate");
System.out.println("leaving ejbPostCreate");
}
private void insertRow(Integer tempPhoneId,
String tempFirstName,
String tempLastName,
String tempPhoneNumber) throws Exception {
System.out.println("in insertRow");
String sqlString = "insert into PHONEPAGES " +
"(PHONE_ID, FIRST_NAME, LAST_NAME, PHONE_NUMBER) values (";

146

code/Chapter6/Phone/PhoneEJB/com/masslight/PhoneNumberEJBClasses/PhoneNumberBean.j
ava
sqlString
sqlString
sqlString
sqlString
sqlString

+=
+=
+=
+=
+=

tempPhoneId;
", '" + tempFirstName;
"', '" + tempLastName;
"', '" + tempPhoneNumber;
"')";

System.out.println("using sql:

" + sqlString);

try {
makeConnection();
connection.setAutoCommit(false);
Statement s = connection.createStatement();
s.executeUpdate(sqlString);
connection.commit();
s.close();
closeConnection();
} catch (SQLException e) {
try {
connection.rollback();
} catch (SQLException e2) {
throw new Exception("Big problems, error committing, error rolling
back!!!");
}
throw new Exception("Error inserting Phone into database");
}
System.out.println("leaving insertRow");
}
/////////////////////////////////////////////////////////////////////////////
public void ejbRemove() {
System.out.println("in ejbRemove");
try {
deleteRow(this.phoneId);
} catch (Exception ex) {
throw new EJBException("ejbRemove: " + ex.getMessage());
}
System.out.println("leaving ejbRemove");
}
private void deleteRow(Integer tempPhoneId) {
System.out.println("in deleteRow");
String sqlString = "delete from PHONEPAGES where PHONE_ID = ";
sqlString += tempPhoneId;

147

code/Chapter6/Phone/PhoneEJB/com/masslight/PhoneNumberEJBClasses/PhoneNumberBean.j
ava
System.out.println("using sql:

" + sqlString);

try {
makeConnection();
connection.setAutoCommit(false);
Statement s = connection.createStatement();
s.executeUpdate(sqlString);
connection.commit();
s.close();
closeConnection();
} catch (SQLException e) {
try {
connection.rollback();
} catch (SQLException e2) {
System.out.println("Big problems, error committing, error rolling
back!!!");
}
System.out.println("Error deleting Phone from database");
}
System.out.println("leaving deleteRow");
}
/////////////////////////////////////////////////////////////////////////////
public void ejbLoad() {
System.out.println("in ejbLoad");
setPhoneId((Integer)(context.getPrimaryKey()));
try {
loadRow(getPhoneId());
} catch (Exception ex) {
throw new EJBException("ejbLoad: " + ex.getMessage());
}
System.out.println("leaving ejbLoad");
}
private void loadRow(Integer tempPhoneId) throws Exception{
System.out.println("in loadRow");
try {
makeConnection();
String sqlString = "select FIRST_NAME, LAST_NAME, PHONE_NUMBER " +
"from PHONEPAGES where PHONE_ID=";
sqlString += tempPhoneId;
Statement s = connection.createStatement();
s.executeQuery(sqlString);
ResultSet rs = s.getResultSet();
if (rs.next()) {
this.firstName = rs.getString(1);
this.lastName = rs.getString(2);
this.phoneNumber = rs.getString(3);
}
s.close();

148

code/Chapter6/Phone/PhoneEJB/com/masslight/PhoneNumberEJBClasses/PhoneNumberBean.j
ava

closeConnection();
} catch (Exception e) {
throw new Exception("Error fetching Phone from database");
}
System.out.println("leaving loadRow");

/////////////////////////////////////////////////////////////////////////////
public void ejbStore() {
System.out.println("in ejbStore");
setPhoneId((Integer)(context.getPrimaryKey()));
try {
storeRow(getPhoneId(), getFirstName(), getLastName(),
getPhoneNumber());
} catch (Exception ex) {
throw new EJBException("ejbLoad: " + ex.getMessage());
}
System.out.println("leaving ejbStore");
}
private void storeRow(Integer tempPhoneId,
String tempFirstName,
String tempLastName, String tempPhoneNumber) {
System.out.println("in storeRow");
String sqlString = "update PHONEPAGES set FIRST_NAME = '";
sqlString += tempFirstName;
sqlString += "'" + ", LAST_NAME = '";
sqlString += tempLastName;
sqlString += "'" + ", PHONE_NUMBER = '";
sqlString += tempPhoneNumber;
sqlString += "'";
sqlString += " where PHONE_ID = ";
sqlString += tempPhoneNumber;
try {
makeConnection();
connection.setAutoCommit(false);
Statement s = connection.createStatement();
s.executeUpdate(sqlString);
connection.commit();
s.close();
closeConnection();
} catch (SQLException e) {
try {
connection.rollback();

149

code/Chapter6/Phone/PhoneEJB/com/masslight/PhoneNumberEJBClasses/PhoneNumberBean.j
ava
} catch (SQLException e2) {
System.out.println("Big problems, error committing, error rolling
back!!!");

e2.printStackTrace();
}
System.out.println("Error updating Phone in database");

}
System.out.println("leaving storeRow");
}
{

public Integer ejbFindByPrimaryKey(Integer tempPhoneID) throws FinderException


System.out.println("in ejbFindByPrimaryKey");
System.out.println(tempPhoneID);
boolean result = false;
try {
result = selectByPrimaryKey(tempPhoneID);
} catch (Exception ex) {
throw new EJBException("ejbFindByPrimaryKey: " + ex.getMessage());
}

if (result) {
System.out.println("leaving ejbFindByPrimaryKey with " + tempPhoneID);
return tempPhoneID;
} else {
throw new ObjectNotFoundException("Row for id " + tempPhoneID + " not
found.");
}
}
private boolean selectByPrimaryKey(Integer tempPhoneID) throws Exception{
System.out.println("in selectByPrimaryKey");
try {
makeConnection();
String sqlString = "select FIRST_NAME, LAST_NAME, PHONE_NUMBER " +
"from PHONEPAGES where PHONE_ID=" + tempPhoneID;
Statement s = connection.createStatement();
s.executeQuery(sqlString);
ResultSet rs = s.getResultSet();
if (rs.next()) {
this.firstName = rs.getString(1);
this.lastName = rs.getString(2);
this.phoneNumber = rs.getString(3);
this.phoneId = tempPhoneID;
}
s.close();

150

code/Chapter6/Phone/PhoneEJB/com/masslight/PhoneNumberEJBClasses/PhoneNumberBean.j
ava
closeConnection();
} catch (SQLException e) {
throw new Exception("Error fetching Phone from database");
}
System.out.println("leaving selectByPrimaryKey with " + tempPhoneID);
}

return true;

public void setEntityContext(EntityContext context) {


System.out.println("in setEntityContext");
this.context = context;
System.out.println("leaving setEntityContext");
}
public void unsetEntityContext() {
System.out.println("in unsetEntityContext");
this.context = null;
System.out.println("leaving unsetEntityContext");
}
public void ejbActivate() {
System.out.println("in ejbActivate");
System.out.println("leaving ejbActivate");
}
public void ejbPassivate() {
System.out.println("in ejbPassivate");
System.out.println("leaving ejbPassivate");
}
public Integer getPhoneId() {
System.out.println("in getPhoneId");
System.out.println("leaving getPhoneId with " + this.phoneId);
return this.phoneId;
}
public String getFirstName() {
System.out.println("in getFirstName");
System.out.println("leaving getName with " + this.firstName);
return this.firstName;
}
public String getLastName() {
System.out.println("in getLastName");
System.out.println("leaving getPhoneId with " + this.lastName);
return this.lastName;
}
public String getPhoneNumber() {
System.out.println("in getPhoneNumbers");
System.out.println("leaving getName with " + this.phoneNumber);
return this.phoneNumber;
}

151

code/Chapter6/Phone/PhoneEJB/com/masslight/PhoneNumberEJBClasses/PhoneNumberBean.j
ava
public void setPhoneId(Integer value) {
this.phoneId = value;
}
public void setFirstName(String value) {
this.firstName = new String(value);
}
public void setLastName(String value) {
this.lastName = value;
}
public void setPhoneNumber(String value) {
this.phoneNumber = new String(value);
}
}

6. Create the ejb-jar.xml file.


code/Chapter6/Phone/PhoneEJB/META-INF/ejb-jar.xml
<?xml version="1.0" encoding="Cp1252"?>
<!DOCTYPE ejb-jar PUBLIC
'-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN'
'http://java.sun.com/dtd/ejb-jar_2_0.dtd'>
<ejb-jar>
<display-name>Phone</display-name>
<enterprise-beans>
<entity>
<display-name>Phone</display-name>
<ejb-name>Phone</ejb-name>
<home>com.masslight.PhoneNumberEJBClasses.PhoneNumberHome</home>
<remote>com.masslight.PhoneNumberEJBClasses.PhoneNumber</remote>
<ejbclass>com.masslight.PhoneNumberEJBClasses.PhoneNumberBean</ejb-class>
<persistence-type>Bean</persistence-type>
<prim-key-class>java.lang.Integer</prim-key-class>
<reentrant>False</reentrant>
<security-identity>
<description></description>
<use-caller-identity></use-caller-identity>
</security-identity>
</entity>
</enterprise-beans>
<assembly-descriptor>
<container-transaction>
<method>
<ejb-name>Phone</ejb-name>

152

code/Chapter6/Phone/PhoneEJB/META-INF/ejb-jar.xml
<method-intf>Home</method-intf>
<method-name>remove</method-name>
<method-params>
<method-param>java.lang.Object</method-param>
</method-params>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
<container-transaction>
<method>
<ejb-name>Phone</ejb-name>
<method-intf>Home</method-intf>
<method-name>findByPrimaryKey</method-name>
<method-params>
<method-param>java.lang.Integer</method-param>
</method-params>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
<container-transaction>
<method>
<ejb-name>Phone</ejb-name>
<method-intf>Remote</method-intf>
<method-name>getPhoneNumber</method-name>
<method-params />
</method>
<trans-attribute>NotSupported</trans-attribute>
</container-transaction>
<container-transaction>
<method>
<ejb-name>Phone</ejb-name>
<method-intf>Home</method-intf>
<method-name>remove</method-name>
<method-params>
<method-param>javax.ejb.Handle</method-param>
</method-params>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
<container-transaction>
<method>
<ejb-name>Phone</ejb-name>
<method-intf>Home</method-intf>
<method-name>create</method-name>
<method-params>
<method-param>java.lang.Integer</method-param>
<method-param>java.lang.String</method-param>
<method-param>java.lang.String</method-param>
<method-param>java.lang.String</method-param>
</method-params>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>

153

code/Chapter6/Phone/PhoneEJB/META-INF/ejb-jar.xml
<container-transaction>
<method>
<ejb-name>Phone</ejb-name>
<method-intf>Remote</method-intf>
<method-name>getLastName</method-name>
<method-params />
</method>
<trans-attribute>NotSupported</trans-attribute>
</container-transaction>
<container-transaction>
<method>
<ejb-name>Phone</ejb-name>
<method-intf>Remote</method-intf>
<method-name>getPhoneId</method-name>
<method-params />
</method>
<trans-attribute>NotSupported</trans-attribute>
</container-transaction>
<container-transaction>
<method>
<ejb-name>Phone</ejb-name>
<method-intf>Remote</method-intf>
<method-name>getFirstName</method-name>
<method-params />
</method>
<trans-attribute>NotSupported</trans-attribute>
</container-transaction>
<container-transaction>
<method>
<ejb-name>Phone</ejb-name>
<method-intf>Remote</method-intf>
<method-name>remove</method-name>
<method-params />
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
</assembly-descriptor>
</ejb-jar>

7. Create the build scripts: one in PhoneClient for the client code, one in PhoneEJB
for the EJB code, and one in Phone that ties them both together.
code/Chapter6/Phone/PhoneClient/build.xml
<project name="PhoneClient" default="dist" basedir=".">
<!-- set global properties for this build -->
<property environment="env"/>
<property name="top" value="."/>

154

code/Chapter6/Phone/PhoneClient/build.xml
<property
<property
<property
<property
<property

name="src" value="."/>
name="build" value="build"/>
name="dist" value="dist"/>
name="war_dir" value="${dist}/lib"/>
name="war_file" value="${war_dir}/PhoneClient.war"/>

<property name="webinf" value="${top}/WEB-INF"/>


<property name="web.xml" value="${webinf}/web.xml"/>
<property name="classes" value="${webinf}/classes"/>
<property name="lib" value="${top}/WEB-INF/lib"/>
<property name="struts.jar" value="${env.STRUTS_HOME}/lib/struts.jar"/>
<property name="servlet.jar" value="$
{env.TOMCAT_HOME}/lib/servlet.jar"/>
<property name="ejb.src" value="${top}/../PhoneEJB"/>
<property name="ejb.jar" value="${env.JBOSS_HOME}/lib/ext/jbossj2ee.jar"/>
<property name="deploy" value="${env.JBOSS_HOME}/deploy"/>
<target name="clean">
<!-- Delete our the ${build} and ${dist} directory trees -->
<delete dir="${build}"/>
<delete dir="${dist}"/>
<delete dir="${war_dir}"/>
</target>
<target name="init">
<!-- Create the build directory structure used by compile and dist
-->
<mkdir dir="${build}"/>
<mkdir dir="${dist}"/>
<mkdir dir="${war_dir}"/>
</target>
<target name="compile" depends="init">
<!-- Compile the java code from ${src} into ${build} -->
<javac
srcdir="${ejb.src}"
destdir="${build}"
classpath="${ejb.jar}"/>
<javac
srcdir="${top}/${src}"
destdir="${build}"
classpath="${servlet.jar}:${struts.jar}:${ejb.jar}"/>
</target>
<target name="dist" depends="compile">
<!-- Put everything in a war file -->
<war warfile="${war_file}" webxml="${web.xml}">
<!-- include all JSPs in root level, and all .properties files
anywhere -->
<fileset dir="${top}/${src}">
<include name="*.jsp"/>
<include name="**/*.properties"/>

155

code/Chapter6/Phone/PhoneClient/build.xml
</fileset>
<!-- include all tag libraries in WEB-INF, and all .xml config
files,
but not web.xml (that's handled separately) -->
<webinf dir="${webinf}">
<include name="*.tld"/>
<include name="*.xml"/>
<exclude name="web.xml"/>
</webinf>
<!-- include all libraries in WEB-INF/lib (like struts.jar) -->
<lib dir="${lib}"/>
<!-- include all compiled classes -->
<classes dir="${build}"/>
</war>
</target>
<target name="deploy">
<!-- Copy the war file to the JBoss deploy directory -->
<copy file="${war_file}" todir="${deploy}"/>
</target>
<target name="all" depends="clean,dist,deploy"/>
</project>

code/Chapter6/Phone/PhoneEJB/build.xml
<project name="PhoneEJB" default="dist" basedir=".">
<!-- set global properties for this build -->
<property environment="env"/>
<property name="top" value="."/>
<property name="src" value="."/>
<property name="build" value="build"/>
<property name="dist" value="dist"/>
<property name="jar_dir" value="${dist}/lib"/>
<property name="jar_file" value="${jar_dir}/PhoneEJB.jar"/>
<property name="webinf" value="${top}/WEB-INF"/>
<property name="web.xml" value="${webinf}/web.xml"/>
<property name="classes" value="${webinf}/classes"/>
<property name="lib" value="${top}/WEB-INF/lib"/>
<property name="ejb.jar" value="${env.JBOSS_HOME}/lib/ext/jbossj2ee.jar"/>
<property name="deploy" value="${env.JBOSS_HOME}/deploy"/>
<target name="clean">

156

code/Chapter6/Phone/PhoneEJB/build.xml
<!-- Delete our the ${build} and ${dist} directory trees -->
<delete dir="${build}"/>
<delete dir="${dist}"/>
<delete dir="${jar_dir}"/>
</target>
<target name="init">
<!-- Create the build directory structure used by compile and dist
-->
<mkdir dir="${build}"/>
<mkdir dir="${dist}"/>
<mkdir dir="${jar_dir}"/>
</target>
<target name="compile" depends="init">
<!-- Compile the java code from ${src} into ${build} -->
<javac
srcdir="${top}/${src}"
destdir="${build}"
classpath="${ejb.jar}"/>
</target>
<target name="dist" depends="compile">
<!-- Put everything in a jar file -->
<jar jarfile="${jar_file}">
<!-- include all compiled class files -->
<fileset dir="${build}">
<include name="**/*.class"/>
</fileset>
<!-- include ejb-jar.xml -->
<fileset dir="${top}">
<include name="META-INF/ejb-jar.xml"/>
</fileset>
</jar>
</target>
<target name="deploy">
<!-- Copy the jar file to the JBoss deploy directory -->
<copy file="${jar_file}" todir="${deploy}"/>
</target>
<target name="all" depends="clean,dist,deploy"/>
</project>

157

code/Chapter6/Phone/build.xml
<project name="Phone" default="all" basedir=".">
<target name="all">
<ant dir="PhoneClient" target="all"/>
<ant dir="PhoneEJB" target="all"/>
</target>
<target name="clean">
<ant dir="PhoneClient" target="clean"/>
<ant dir="PhoneEJB" target="clean"/>
</target>
</project>

8. Create the database. This uses the OpenBase Manager program.


o Run OpenBase Manager.
o From the Tools menu, select 'Restore from ASCII'.
o Select phone.bck from the databases directory of your course material.
o Database name: phone (all lowercase! OpenBase is case-sensitive!)
o Check 'Start database at boot'
o Click 'Set' to create the database.
o Wait a few seconds while OpenBase creates the database and imports the
data (there is no progress indicator).
o Select the database from the list, once it shows up.
o Click 'Start' to start the database manually. You only have to do this once;
it should auto-start whenever you boot your computer.
databases/phone.bck
delete from _SYS_APPS
go
delete from _SYS_GROUPS
go
create table _SYS_JDBC2v1 (
_rowid longlong NOT NULL UNIQUE ,
_timestamp datetime ,
_version long ,
typename char(50) ,
type int ,
searchable int ,
precision int ,
max_scale int ,
order int )
go
delete from _SYS_PERM
go
delete from _SYS_RELATIONSHIP

158

databases/phone.bck
go
delete from _SYS_REPLICATION
go
delete from _SYS_USERS
go
create table PHONEPAGES (
_rowid longlong NOT NULL UNIQUE ,
_timestamp datetime ,
_version long ,
PHONE_NUMBER char(20) ,
FIRST_NAME char(80) ,
LAST_NAME char(80) ,
PHONE_ID int NOT NULL )
go
TABLE _SYS_APPS
_rowid, _timestamp, _version, appName
'2' , '30308263.048366427421' , '1' , 'JDBC'
'3' , '30308275.586395144462' , '1' , 'OpenBaseManager'
TABLE _SYS_GROUPS
_rowid, _timestamp, _version, appAuthorizationEnabled, authorizedApps,
blockedRadApps, group
'1' , '30308240.375764846801' , '1' , NULL, NULL, NULL, 'admin'
TABLE _SYS_JDBC2v1
_rowid, _timestamp, _version, max_scale, order, precision, searchable,
type, typename
'1' , '30308240.465894341468' , '1' , '0' , '0' , '0' , '3' , '1' ,
'char'
'2' , '30308240.465894341468' , '1' , '0' , '0' , '10' , '2' , '4' ,
'int'
'3' , '30308240.465894341468' , '1' , '0' , '0' , '0' , '1' , '-4' ,
'object'
'4' , '30308240.465894341468' , '1' , '0' , '0' , '20' , '2' , '-5' ,
'longlong'
'5' , '30308240.475908756256' , '1' , '0' , '1' , '20' , '2' , '8' ,
'double'
'6' , '30308240.475908756256' , '1' , '6' , '0' , '20' , '2' , '93' ,
'datetime'
'7' , '30308240.475908756256' , '1' , '2' , '0' , '20' , '2' , '3' ,
'money'
'8' , '30308240.475908756256' , '1' , '15' , '0' , '20' , '2' , '8' ,
'float'
'9' , '30308240.475908756256' , '1' , '0' , '1' , '10' , '2' , '4' ,
'long'
'10' , '30308240.475908756256' , '1' , '0' , '0' , '0' , '2' , '91' ,
'date'
'11' , '30308240.475908756256' , '1' , '0' , '0' , '0' , '2' , '92' ,
'time'
TABLE _SYS_PERM
_rowid, _timestamp, _version, name, p_delete, p_insert, p_select,
p_update, table
TABLE _SYS_REPLICATION
_rowid, _timestamp, _version, conflict_code, local_date, remote_date,
tablename, target_database, target_host, target_password

159

databases/phone.bck
TABLE _SYS_USERS
_rowid, _timestamp, _version, appSecurity, authorizedApps, email,
fullname, login, password, popLogin, popPassword, popServer, smtpServer,
usergroup
'1' , '30308240.355736017227' , '1' , '0' , NULL, NULL, 'admin' ,
'admin' , '' , NULL, NULL, NULL, NULL, 'admin'
TABLE PHONEPAGES
_rowid, _timestamp, _version, FIRST_NAME, LAST_NAME, PHONE_ID,
PHONE_NUMBER
'1' , '11733756.967038359493' , '9' , 'Mark' , 'Pilgrim' , '1' , '5551212'
'2' , '11125299.370046524330' , '4' , 'John' , 'Malach' , '2' , '5551213'

9. From the root Phone directory, ant all to compile and deploy the application.
10. Go to http://localhost:8080/PhoneClient/getEJB.do to test your
application.

Exercises
Exercise 1. Name history
Make an EJB-based application where the user enters a name, and the application
displays a list of all the names the user has entered so far. Use a stateful EJB to store the
history of names.
Exercise 2. Database-based name history
Extend the application from exercise 1 to store the history of names in a database table.
When the application starts, the EJB should read the history from the table, and every
time the user enters a name, the EJB should insert it into the database.

Chapter 7: A Case Study

160

The MyCo Project

Goals

After completing this chapter, the student will be able to


understand the use of Enterprise JavaBeans for database access.
use EJBs with the Struts framework.

better understand the J2EE technologies.

In order to complete this chapter successfully, the student must


understand the Struts framework.
have a basic knowledge of SQL.
Prerequisites

Objectives

know the order that methods of an EJB are invoked.

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 faulttolerant 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 Connection Pooling


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.
Setting Up A Connection Pool In JBoss
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

161
previous chapter, that told JBoss about OpenBase. Add the following information just
below the OpenBase driver information in the JDBC section:

<mbean code="org.jboss.jdbc.XADataSourceLoader"
name="DefaultDomain:service=XADataSource,name=OpenBasePool">
<attribute name="DataSourceClass">
org.jboss.pool.jdbc.xa.wrapper.XADataSourceImpl
</attribute>
<attribute name="PoolName">OpenBasePool</attribute>
<attribute name="URL">jdbc:openbase://127.0.0.1/employee</attribute>
<attribute name="JDBCUser">admin</attribute>
<attribute name="Password">admin</attribute>
<attribute name="LoggingEnabled">true</attribute>
</mbean>

Requesting A Database Connection

System.setProperty("java.naming.factory.initial",
"org.jnp.interfaces.NamingContextFactory");
System.setProperty("java.naming.provider.url", "localhost:1099");
InitialContext ic = new InitialContext();
DataSource ds = (DataSource) ic.lookup("java:OpenBasePool");
connection = ds.getConnection();

Releasing The Database Connection

try {
connection.close();
} catch (SQLException e) {
System.out.println("An SQL Exception occurred while closing the
connection");
}

Prepared Statements
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.
Fetching Data

162

String sqlString = "select FIRSTNAME, LASTNAME, EXTENSION, CITY_ID,


DEPARTMENT_ID " +
"from EMPLOYEE where EMPLOYEE_ID=?";
java.sql.PreparedStatement preparedStatement =
connection.prepareStatement(sqlString);
preparedStatement.clearParameters();
preparedStatement.setObject(1, getEmployeeid(), Types.INTEGER);
preparedStatement.executeQuery();

Accessing Fetched Data

ResultSet rs = preparedStatement.getResultSet();
if (rs.next()) {
setFirstname(rs.getString(1));
setLastname(rs.getString(2));
setExtension(rs.getString(3));
setCityid(new Integer(rs.getInt(4)));
setDepartmentid(new Integer(rs.getInt(5)));
}
rs.close();
preparedStatement.close();

Updating Data

try {
String sqlString = "update EMPLOYEE set FIRSTNAME=?, LASTNAME=?, " +
"EXTENSION=?, CITY_ID=?, DEPARTMENT_ID=? where EMPLOYEEID=?";
java.sql.PreparedStatement preparedStatement =
connection.prepareStatement(sqlString);
preparedStatement.clearParameters();
preparedStatement.setObject(1, getFirstname(), Types.VARCHAR);
preparedStatement.setObject(2, getLastname(), Types.VARCHAR);
preparedStatement.setObject(3, getExtension(), Types.VARCHAR);
preparedStatement.setObject(4, getCityid(), Types.INTEGER);
preparedStatement.setObject(5, getDepartmentid(), Types.INTEGER);
preparedStatement.setObject(6, getEmployeeid(), Types.INTEGER);
connection.setAutoCommit(false);
preparedStatement.executeQuery();
connection.commit();
preparedStatement.close();
} catch (SQLException e) {
connection.rollback();
}

Inserting New Data

163

try {
String sqlString = "insert into EMPLOYEE " +
"(EMPLOYEE_ID, FIRSTNAME, LASTNAME, EXTENSION, CITY_ID,
DEPARTMENT_ID) values (" +
"?, ?, ?, ?, ?, ?)";
java.sql.PreparedStatement preparedStatement =
connection.prepareStatement(sqlString);
preparedStatement.clearParameters();
preparedStatement.setObject(1, getEmployeeid(), Types.INTEGER);
preparedStatement.setObject(2, getFirstname(), Types.VARCHAR);
preparedStatement.setObject(3, getLastname(), Types.VARCHAR);
preparedStatement.setObject(4, getExtension(), Types.VARCHAR);
preparedStatement.setObject(5, getCityid(), Types.INTEGER);
preparedStatement.setObject(6, getDepartmentid(), Types.INTEGER);
connection.setAutoCommit(false);
preparedStatement.executeQuery();
connection.commit();
preparedStatement.close();
} catch (SQLException e) {
connection.rollback();
}

Deleting Data

try {
String sqlString = "delete from EMPLOYEE where EMPLOYEE_ID=?";
java.sql.PreparedStatement preparedStatement =
connection.prepareStatement(sqlString);
preparedStatement.clearParameters();
preparedStatement.setObject(1, employeeid, Types.INTEGER);
connection.setAutoCommit(false);
preparedStatement.executeQuery();
connection.commit();
preparedStatement.close();
} catch (SQLException e) {
connection.rollback();
}

MyCo: A Complete J2EE Application


The MyCo Database
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

164
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:
1. Create the database. This uses the OpenBase Manager program.
o Run OpenBase Manager.
o From the Tools menu, select 'Restore from ASCII'.
o Select employee.bck from the databases directory of your course
material.
o Database name: employee (all lowercase! OpenBase is case-sensitive!)
o Check 'Start database at boot'
o Click 'Set' to create the database.
o Wait a few seconds while OpenBase creates the database and imports the
data (there is no progress indicator).
o Select the database from the list, once it shows up.
o Click 'Start' to start the database manually. You only have to do this once;
it should auto-start whenever you boot your computer.

databases/employee.bck
create table _SYS_APP_FIELDS (
_rowid longlong NOT NULL UNIQUE ,
_timestamp datetime ,

165

databases/employee.bck
_version long ,
indexType int ,
name varchar ,
type varchar ,
primaryKey int ,
length int ,
table_id longlong ,
notNull int ,
defaultValue varchar )
go
create table _SYS_APP_PREFERENCES (
_rowid longlong NOT NULL UNIQUE ,
_timestamp datetime ,
_version long ,
key varchar INDEX ,
value varchar ,
order long INDEX )
go
create table _SYS_APP_TABLES (
_rowid longlong NOT NULL UNIQUE ,
_timestamp datetime ,
_version long ,
name varchar ,
x float ,
y float ,
height float ,
width float ,
primaryKeyFieldID longlong ,
clusteredFieldID longlong )
go
delete from _SYS_APPS
go
delete from _SYS_GROUPS
go
create table _SYS_JDBC2v1 (
_rowid longlong NOT NULL UNIQUE ,
_timestamp datetime ,
_version long ,
typename char(50) ,
type int ,
searchable int ,
precision int ,
max_scale int ,
order int )
go
create table _SYS_JDBC4 (
_rowid longlong NOT NULL UNIQUE ,
_timestamp datetime ,
_version long ,
typename char(50) ,
type int ,
searchable int ,
precision int ,

166

databases/employee.bck
max_scale int ,
order int )
go
delete from _SYS_PERM
go
create table _SYS_QUERY (
_rowid longlong NOT NULL UNIQUE ,
_timestamp datetime ,
_version long ,
title char(200) INDEX ,
sql object )
go
delete from _SYS_RELATIONSHIP
go
delete from _SYS_REPLICATION
go
delete from _SYS_USERS
go
create table CITY (
_rowid longlong NOT NULL UNIQUE ,
_timestamp datetime ,
_version long ,
CITY_ID int UNIQUE ,
NAME varchar )
go
CREATE PRIMARY KEY CITY (CITY_ID)
go
create table DEPARTMENT (
_rowid longlong NOT NULL UNIQUE ,
_timestamp datetime ,
_version long ,
PHONE varchar ,
NAME varchar ,
SECRETARY_ID int ,
DEPARTMENT_ID int UNIQUE )
go
CREATE PRIMARY KEY DEPARTMENT (DEPARTMENT_ID)
go
create table EMPLOYEE (
_rowid longlong NOT NULL UNIQUE ,
_timestamp datetime ,
_version long ,
CITY_ID int ,
DEPARTMENT_ID int ,
EMPLOYEE_ID int NOT NULL UNIQUE ,
LASTNAME varchar ,
FIRSTNAME varchar ,
EXTENSION varchar )
go
CREATE PRIMARY KEY EMPLOYEE (EMPLOYEE_ID)
go
create table EO_PK_TABLE (
_rowid longlong NOT NULL UNIQUE ,

167

databases/employee.bck
_timestamp datetime ,
_version long ,
NAME char(40) ,
PK int )
go
TABLE _SYS_APP_FIELDS
_rowid, _timestamp, _version, defaultValue, indexType, length, name, notNull,
primaryKey, table_id, type
'1' , '11207787.889387995004' , '5' , '' , '2' , '4' , 'CITY_ID' , '0' , '1' , '1' ,
'integer'
'2' , '11207787.873592067510' , '5' , '' , '2' , '8' , '_rowid' , '1' , '0' , '1' ,
'longlong'
'3' , '11207787.881354546174' , '5' , '' , '0' , '8' , '_timestamp' , '0' , '0' ,
'1' , 'datetime'
'4' , '11207787.885409614071' , '5' , '' , '0' , '4' , '_version' , '0' , '0' ,
'1' , 'long'
'5' , '11207787.894005864858' , '5' , '' , '0' , '4096' , 'NAME' , '0' , '0' , '1' ,
'char'
'6' , '11207787.995252961292' , '6' , '' , '0' , '4096' , 'LASTNAME' , '0' , '0' ,
'2' , 'char'
'7' , '11207787.975824093446' , '5' , '' , '0' , '4' , '_version' , '0' , '0' ,
'2' , 'long'
'8' , '11207787.979770679026' , '5' , '' , '0' , '4' , 'CITY_ID' , '0' , '0' , '2' ,
'integer'
'9' , '11207787.962303306907' , '5' , '' , '2' , '8' , '_rowid' , '1' , '0' , '2' ,
'longlong'
'10' , '11207787.970909984782' , '5' , '' , '0' , '8' , '_timestamp' , '0' , '0' ,
'2' , 'datetime'
'11' , '11207936.149342946708' , '7' , '' , '0' , '4096' , 'EXTENSION' , '0' , '0' ,
'2' , 'char'
'12' , '11207787.991427551954' , '7' , '' , '2' , '4' , 'EMPLOYEE_ID' , '1' , '1' ,
'2' , 'integer'
'13' , '11207788.003969855606' , '6' , '' , '0' , '4096' , 'FIRSTNAME' , '0' , '0' ,
'2' , 'char'
'14' , '11207787.987668121233' , '5' , '' , '0' , '4' , 'DEPARTMENT_ID' , '0' ,
'0' , '2' , 'integer'
'15' , '11207788.081188667565' , '5' , '' , '0' , '4096' , 'PHONE' , '0' , '0' , '3'
, 'char'
'16' , '11207788.073375049978' , '5' , '' , '0' , '8' , '_timestamp' , '0' , '0' ,
'3' , 'datetime'
'17' , '11207788.064866315573' , '5' , '' , '2' , '8' , '_rowid' , '1' , '0' , '3' ,
'longlong'
'18' , '11207788.084977816790' , '5' , '' , '0' , '4096' , 'NAME' , '0' , '0' ,
'3' , 'char'
'19' , '11207788.088803105056' , '5' , '' , '0' , '4' , 'SECRETARY_ID' , '0' , '0' ,
'3' , 'integer'
'20' , '11207788.077288936823' , '5' , '' , '0' , '4' , '_version' , '0' , '0' , '3'
, 'long'
'21' , '11207788.092554474250' , '5' , '' , '2' , '4' , 'DEPARTMENT_ID' , '0' ,
'1' , '3' , 'integer'
TABLE _SYS_APP_PREFERENCES
_rowid, _timestamp, _version, key, order, value
'1' , '11725615.682600136846' , '9' , 'SchemaDatabaseVersion' , '0' , '1'

168

databases/employee.bck
'2' , '9475504.511265635490' , '1' , 'SchemaVersion' , '0' , ''
'3' , '11207936.239944290370' , '12' , 'SchemaTimeStamp' , '0' , '182;2001-05-10
13:18:56 -0400;3;2001-05-10 13:18:56 -0400;21;2001-05-10 13:18:56 -0400'
TABLE _SYS_APP_TABLES
_rowid, _timestamp, _version, clusteredFieldID, height, name, primaryKeyFieldID,
width, x, y
'1' , '11207936.171712143346' , '11' , '405328' , '75.0000000000000' , 'CITY' ,
NULL, '150.0000000000000' , '204.0000000000000' , '153.0000000000000'
'2' , '11207936.153827134520' , '11' , '405328' , '75.0000000000000' , 'EMPLOYEE' ,
NULL, '150.0000000000000' , '204.0000000000000' , '153.0000000000000'
'3' , '11207936.188525659963' , '10' , '405328' , '75.0000000000000' ,
'DEPARTMENT' , NULL, '150.0000000000000' , '204.0000000000000' , '153.0000000000000'
TABLE _SYS_APPS
_rowid, _timestamp, _version, appName
'2' , '9475502.458313584327' , '1' , 'OpenBaseManager'
'3' , '11125299.483367169275' , '1' , 'JDBC'
TABLE _SYS_GROUPS
_rowid, _timestamp, _version, appAuthorizationEnabled, authorizedApps,
blockedRadApps, group
'1' , '15869401.859825039282' , '3' , '0' , '||' , '||' , 'admin'
TABLE _SYS_JDBC2v1
_rowid, _timestamp, _version, max_scale, order, precision, searchable, type,
typename
'1' , '15869376.265289928764' , '1' , '0' , '0' , '0' , '3' , '1' , 'char'
'2' , '15869376.267148101702' , '1' , '0' , '0' , '10' , '2' , '4' , 'int'
'3' , '15869376.268743053078' , '1' , '0' , '0' , '0' , '1' , '-4' , 'object'
'4' , '15869376.276104232296' , '1' , '0' , '0' , '20' , '2' , '-5' , 'longlong'
'5' , '15869376.277965048328' , '1' , '0' , '1' , '20' , '2' , '8' , 'double'
'6' , '15869376.279329052194' , '1' , '6' , '0' , '20' , '2' , '93' , 'datetime'
'7' , '15869376.280617693440' , '1' , '2' , '0' , '20' , '2' , '3' , 'money'
'8' , '15869376.281898763030' , '1' , '15' , '0' , '20' , '2' , '8' , 'float'
'9' , '15869376.283183138817' , '1' , '0' , '1' , '10' , '2' , '4' , 'long'
'10' , '15869376.284771358594' , '1' , '0' , '0' , '0' , '2' , '91' , 'date'
'11' , '15869376.286046359688' , '1' , '0' , '0' , '0' , '2' , '92' , 'time'
TABLE _SYS_JDBC4
_rowid, _timestamp, _version, max_scale, order, precision, searchable, type,
typename
'1' , '9475499.504065632820' , '1' , '0' , '0' , '0' , '3' , '1' , 'char'
'2' , '9475499.504065632820' , '1' , '0' , '0' , '10' , '2' , '4' , 'int'
'3' , '9475499.514080047607' , '1' , '0' , '0' , '0' , '1' , '-4' , 'object'
'4' , '9475499.514080047607' , '1' , '0' , '0' , '20' , '2' , '-5' , 'longlong'
'5' , '9475499.514080047607' , '1' , '0' , '1' , '20' , '2' , '8' , 'double'
'6' , '9475499.514080047607' , '1' , '6' , '0' , '20' , '2' , '93' , 'datetime'
'7' , '9475499.514080047607' , '1' , '2' , '0' , '20' , '2' , '3' , 'money'
'8' , '9475499.514080047607' , '1' , '15' , '0' , '20' , '2' , '8' , 'float'
'9' , '9475499.514080047607' , '1' , '0' , '1' , '10' , '2' , '4' , 'long'
'10' , '9475499.524094343185' , '1' , '0' , '0' , '0' , '2' , '91' , 'date'
'11' , '9475499.524094343185' , '1' , '0' , '0' , '0' , '2' , '92' , 'time'
TABLE _SYS_PERM
_rowid, _timestamp, _version, name, p_delete, p_insert, p_select, p_update, table
TABLE _SYS_QUERY
_rowid, _timestamp, _version, sql, title
OBJECT

169

databases/employee.bck
'<<<<*1*>>>>'
object
78
53454C4543542074302E5F726F7769642C2074302E5F74696D657374616D702C2074302E5F7665727369
6F6E2C2074302E434954595F49442C2074302E4E414D452046524F4D2043495459207430
.end.
'1' , '10515524.025541719050' , '2' , '<<<<*1*>>>>' , 'CityQuery'
OBJECT
'<<<<*2*>>>>'
object
111
53454C4543542074302E5F726F7769642C2074302E5F74696D657374616D702C2074302E5F7665727369
6F6E2C2074302E444550545F49442C2074302E4E414D452C2074302E50484F4E452C2074302E53454352
45544152595F49442046524F4D204445504152544D454E54207430
.end.
'2' , '10515536.128062479197' , '1' , '<<<<*2*>>>>' , 'DepartmentQuery'
OBJECT
'<<<<*3*>>>>'
object
137
53454C4543542074302E5F726F7769642C2074302E5F74696D657374616D702C2074302E5F7665727369
6F6E2C2074302E434954595F49442C2074302E444550545F49442C2074302E454D505F49442C2074302E
455854454E53494F4E2C2074302E46495253544E414D452C2074302E4C4153544E414D452046524F4D20
454D504C4F594545207430
.end.
'3' , '10515545.771298721432' , '1' , '<<<<*3*>>>>' , 'EmployeeQuery'
TABLE _SYS_REPLICATION
_rowid, _timestamp, _version, conflict_code, local_date, remote_date, tablename,
target_database, target_host, target_password
TABLE _SYS_USERS
_rowid, _timestamp, _version, appSecurity, authorizedApps, email, fullname, login,
password, popLogin, popPassword, popServer, smtpServer, usergroup
'1' , '15869401.867665303871' , '3' , '0' , NULL, '' , 'Administrator' , 'admin' ,
'DIAJDIGDHDBCJDCI' , '' , '' , '' , '' , 'admin'
TABLE CITY
_rowid, _timestamp, _version, CITY_ID, NAME
'1' , '11733756.967038359493' , '9' , '1' , 'Orlando'
'2' , '11125299.370046524330' , '4' , '2' , 'New York'
'3' , '11125299.436561474576' , '6' , '3' , 'Toronto'
'5' , '11125299.423119541257' , '4' , '5' , 'San Luis Obispo'
'6' , '11125299.429148418828' , '5' , '6' , 'Salt Lake City'
'7' , '11125299.441440487280' , '4' , '7' , 'Kansas City'
'8' , '11125299.440038463100' , '3' , '8' , 'Bismarck'
'10' , '11296994.582311162725' , '1' , '300' , 'Springfield'
'11' , '11733755.998177478090' , '3' , '11' , 'Washington DC'
TABLE DEPARTMENT
_rowid, _timestamp, _version, DEPARTMENT_ID, NAME, PHONE, SECRETARY_ID
'1' , '11125299.410846965387' , '12' , '1' , 'Sales' , '5521' , NULL
'2' , '11125299.409506827592' , '4' , '2' , 'Engineering' , '5531' , NULL
TABLE EMPLOYEE
_rowid, _timestamp, _version, CITY_ID, DEPARTMENT_ID, EMPLOYEE_ID, EXTENSION,
FIRSTNAME, LASTNAME
'52' , '21482695.351134277880' , '1' , '2' , '2' , '90' , '9032' , 'Mark' ,

170

databases/employee.bck
'Yellowford'
'33' , '11824000.298210268840' , '1' ,
'Mullholland'
'32' , '11733864.158240400254' , '1' ,
'Foresight'
'31' , '11733666.934869606047' , '1' ,
'Poll'
'30' , '16134490.803055632860' , '5' ,
'Varquee'
'26' , '11733875.436699779704' , '4' ,
'Muskrat'
'25' , '11824025.344245314598' , '3' ,
'Beads'
'24' , '11725296.852966878563' , '3' ,
'Reynolds'
'23' , '11725305.505942394956' , '4' ,
'Colburn'
'22' , '11721548.348074020817' , '1' ,
'20' , '16132498.553308600560' , '4' ,
'Turner'
'39' , '16134802.706042600795' , '2' ,
'40' , '16132589.374745823442' , '2' ,
'Strongman'
'44' , '21482777.570284608751' , '4' ,
'Wensleydale'
'48' , '16149047.240697329863' , '1' ,
'Piper'
'49' , '16149221.292628999800' , '3' ,
'Bonn'
'50' , '17162220.083476159721' , '1' ,
'Pecan'
TABLE EO_PK_TABLE
_rowid, _timestamp, _version, NAME, PK
'1' , '16149047.143503218890' , '10' ,

'3' , '2' , '7878' , '2932' , 'Katie' ,


'11' , '2' , '69' , '6969' , 'Stan' ,
'6' , '2' , '59' , '5555' , 'Catalin' ,
'5' , '2' , '89' , '2222' , 'Quentin' ,
'11' , '1' , '2' , '9023' , 'Elisabeth' ,
'1' , '1' , '6' , '9932' , 'Juniper' ,
'6' , '1' , '7' , '2930' , 'Debbie' ,
'8' , '2' , '5' , '6609' , 'Marilyn' ,
'7' , '2' , '3' , '4004' , 'Colby' , 'Smith'
'1' , '2' , '1' , '1001' , 'Joseph' ,
'1' , '1' , '9095' , '23' , 'Tracy' , 'Davis'
'1' , '1' , '9096' , '0033' , 'Michael' ,
'1' , '1' , '666' , '666' , 'Jonathan' ,
'1' , '1' , '9102' , '1234' , 'Peter' ,
'5' , '2' , '888' , '9876' , 'Theodore' ,
'5' , '2' , '9890' , '9923' , 'Gweneth' ,

'EMPLOYEE' , '9102'

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.

171

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

172

code/Chapter7/Employee
|
|
|
|
|
+-- 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 (*)

173

code/Chapter7/Employee
|
+-- Department
|
|
|
+-- Department.java (*)
|
|
|
+-- ejb-jar.xml (*)
|
|
|
+-- DepartmentHome.java (*)
|
|
|
+-- DepartmentEJB.java (*)
|
+-- City
|
+-- City.java (*)
|
+-- CityHome.java (*)
|
+-- ejb-jar.xml (*)
|
+-- CityEJB.java (*)
(*) denotes a file

Employee EJB
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

174
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();

175
// 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
package com.masslight.Employee;
import com.masslight.City.CityHome;
import com.masslight.City.City;
import com.masslight.Department.DepartmentHome;
import com.masslight.Department.Department;
import javax.ejb.EJBObject;
import java.rmi.RemoteException;
public interface Employee extends EJBObject {
public Integer getEmployeeid() throws RemoteException;

176

code/Chapter7/Employee/EmployeeEJB/com/masslight/Employee/Employee.java
public
public
public
public
public
public
public
public
public
public
public
public
public
public
public

void setEmployeeid(Integer value) throws RemoteException;


String getLastname() throws RemoteException;
void setLastname(String value) throws RemoteException;
String getFirstname() throws RemoteException;
void setFirstname(String value) throws RemoteException;
String getExtension() throws RemoteException;
void setExtension(String value) throws RemoteException;
Integer getDepartmentid() throws RemoteException;
void setDepartmentid(Integer value) throws RemoteException;
Integer getCityid() throws RemoteException;
void setCityid(Integer value) throws RemoteException;
City getCity() throws RemoteException;
void setCity(City value) throws RemoteException;
Department getDepartment() throws RemoteException;
void setDepartment(Department value) throws RemoteException;

code/Chapter7/Employee/EmployeeEJB/com/masslight/Employee/EmployeeHome.java
package com.masslight.Employee;
import java.util.Collection;
import java.rmi.RemoteException;
import javax.ejb.*;
public interface EmployeeHome extends EJBHome {
public Employee create(Integer employeeid,
String firstname,
String lastname,
String extension,
Integer cityid,
Integer departmentid)
throws RemoteException, CreateException;
public Employee findByPrimaryKey(Integer employeeid)
throws FinderException, RemoteException;
public Collection findAll()
throws FinderException, RemoteException;
}

177

code/Chapter7/Employee/EmployeeEJB/com/masslight/Employee/EmployeeEJB.java
package com.masslight.Employee;
import com.masslight.City.CityHome;
import com.masslight.City.City;
import com.masslight.Department.DepartmentHome;
import com.masslight.Department.Department;
import
import
import
import
import
import

java.sql.*;
java.util.Enumeration;
java.util.Vector;
java.util.Collection;
java.util.Iterator;
javax.ejb.*;

import java.util.Properties;
import javax.rmi.PortableRemoteObject;
import javax.naming.*;
import java.rmi.RemoteException;
import javax.sql.*;
public class EmployeeEJB implements EntityBean {
private EntityContext context = null;
private Connection connection = null;
private
private
private
private
private
private

Integer employeeid = new Integer(0);


String firstname = new String();
String lastname = new String();
String extension = new String();
Integer cityid;
Integer departmentid;

////////////////////////////////////////////////////////////////////////////

public void makeConnection() {


try {
System.setProperty("java.naming.factory.initial",
"org.jnp.interfaces.NamingContextFactory");
System.setProperty("java.naming.provider.url", "localhost:1099");
InitialContext ic = new InitialContext();
if(ic == null) throw new Exception("initial context is null");
DataSource ds = (DataSource) ic.lookup("java:OpenBasePool");
if(ds != null) {
connection = ds.getConnection();
} else {
throw new Exception("datasource is null");
}

178

code/Chapter7/Employee/EmployeeEJB/com/masslight/Employee/EmployeeEJB.java

} catch(javax.naming.NamingException exc) {
System.out.println("naming exception: "+exc.getMessage());
} catch(java.sql.SQLException ex) {
System.out.println("sql exception: "+ex.getMessage());
} catch(Exception except) {
System.out.println(except.getMessage());
}

////////////////////////////////////////////////////////////////////////////
/
public void closeConnection() {
try {
connection.close();
} catch (SQLException e) {
System.out.println("An SQL Exception occurred while closing the
connection");
}
}
////////////////////////////////////////////////////////////////////////////
/
public Integer ejbCreate(Integer employeeid,
String firstname,
String lastname,
String extension,
Integer cityid,
Integer departmentid) throws CreateException {
try {
insertRow(employeeid, firstname, lastname, extension, cityid,
departmentid);
} catch (Exception ex) {
throw new EJBException("ejbCreate: " + ex.getMessage());
}
setEmployeeid(employeeid);
setFirstname(firstname);
setLastname(lastname);
setExtension(extension);
setCityid(cityid);
setDepartmentid(departmentid);
return employeeid;
}
public void ejbPostCreate(Integer employeeid,
String firstname,
String lastname,
String extension,
Integer cityid,
Integer departmentid) {
}

179

code/Chapter7/Employee/EmployeeEJB/com/masslight/Employee/EmployeeEJB.java
private void insertRow(Integer employeeid,
String firstname,
String lastname,
String extension,
Integer cityid,
Integer departmentid) {
String sqlString = "insert into EMPLOYEE " +
"(EMPLOYEE_ID, FIRSTNAME, LASTNAME, EXTENSION, CITY_ID, DEPARTMENT_ID)
values (";
sqlString += employeeid;
sqlString += ", '" + firstname;
sqlString += "', '" + lastname;
sqlString += "', '" + extension;
sqlString += "', " + cityid;
sqlString += ", " + departmentid;
sqlString += ")";
try {
connection.setAutoCommit(false);
Statement s = connection.createStatement();
s.executeUpdate(sqlString);
connection.commit();
s.close();
} catch (SQLException e) {
try {
connection.rollback();
} catch (SQLException e2) {
System.out.println("Big problems, error committing, error rolling
back!!!");
}
System.out.println("Error inserting Employee into database");
}
}
////////////////////////////////////////////////////////////////////////////

public void ejbRemove() {


try {
deleteRow(getEmployeeid());
} catch (Exception ex) {
throw new EJBException("ejbRemove: " + ex.getMessage());
}
}
private void deleteRow(Integer employeeid) {
System.out.println("EmployeeEJB.deleteRow: enter, employeeid=" +
employeeid);

180

code/Chapter7/Employee/EmployeeEJB/com/masslight/Employee/EmployeeEJB.java
try {
String sqlString = "delete from EMPLOYEE where EMPLOYEE_ID=?";
java.sql.PreparedStatement preparedStatement =
connection.prepareStatement(sqlString);
preparedStatement.clearParameters();
preparedStatement.setObject(1, employeeid, Types.INTEGER);
connection.setAutoCommit(false);
preparedStatement.executeQuery();
connection.commit();
preparedStatement.close();
} catch (SQLException e) {
try {
connection.rollback();
} catch (SQLException e2) {
System.out.println("Big problems, error committing, error
rolling back!!!");
}
System.out.println("Error deleting Employee from database");
}
System.out.println("EmployeeEJB.deleteRow: exit");
}
////////////////////////////////////////////////////////////////////////////

public void ejbLoad() {


setEmployeeid((Integer)(context.getPrimaryKey()));
try {
loadRow();
} catch (Exception ex) {
throw new EJBException("ejbLoad: " + ex.getMessage());
}
}
private void loadRow() {
System.out.println("EmployeeEJB.loadRow: enter, employeeid=" +
getEmployeeid());
try {
String sqlString = "select FIRSTNAME, LASTNAME, EXTENSION, CITY_ID,
DEPARTMENT_ID " +
"from EMPLOYEE where EMPLOYEE_ID=?";
java.sql.PreparedStatement preparedStatement =
connection.prepareStatement(sqlString);
preparedStatement.clearParameters();
preparedStatement.setObject(1, getEmployeeid(), Types.INTEGER);
preparedStatement.executeQuery();
ResultSet rs = preparedStatement.getResultSet();
if (rs.next()) {
setFirstname(rs.getString(1));
setLastname(rs.getString(2));
setExtension(rs.getString(3));

181

code/Chapter7/Employee/EmployeeEJB/com/masslight/Employee/EmployeeEJB.java
setCityid(new Integer(rs.getInt(4)));
setDepartmentid(new Integer(rs.getInt(5)));

}
rs.close();
preparedStatement.close();
} catch (SQLException e) {
System.out.println("Error fetching Employee from database");
}
System.out.println("EmployeeEJB.loadRow: exit");

////////////////////////////////////////////////////////////////////////////
/
public void ejbStore() {
setEmployeeid((Integer)(context.getPrimaryKey()));
try {
storeRow();
} catch (Exception ex) {
throw new EJBException("ejbLoad: " + ex.getMessage());
}
}
private void storeRow() {
System.out.println("EmployeeEJB.storeRow: enter, employeeid=" +
getEmployeeid());
try {
String sqlString = "update EMPLOYEE set FIRSTNAME=?, LASTNAME=?, " +
"EXTENSION=?, CITY_ID=?, DEPARTMENT_ID=? where EMPLOYEEID=?";
java.sql.PreparedStatement preparedStatement =
connection.prepareStatement(sqlString);
preparedStatement.clearParameters();
preparedStatement.setObject(1, getFirstname(), Types.VARCHAR);
preparedStatement.setObject(2, getLastname(), Types.VARCHAR);
preparedStatement.setObject(3, getExtension(), Types.VARCHAR);
preparedStatement.setObject(4, getCityid(), Types.INTEGER);
preparedStatement.setObject(5, getDepartmentid(), Types.INTEGER);
preparedStatement.setObject(6, getEmployeeid(), Types.INTEGER);
connection.setAutoCommit(false);
preparedStatement.executeQuery();
connection.commit();
preparedStatement.close();
} catch (SQLException e) {
try {
connection.rollback();
} catch (SQLException e2) {
System.out.println("Big problems, error committing, error
rolling back!!!");
}

182

code/Chapter7/Employee/EmployeeEJB/com/masslight/Employee/EmployeeEJB.java

System.out.println("Error updating Employee in database");


}
System.out.println("EmployeeEJB.storeRow: exit");

////////////////////////////////////////////////////////////////////////////
/
public Integer ejbFindByPrimaryKey(Integer employeeid) throws
FinderException {
boolean result;
try {
result = selectByPrimaryKey(employeeid);
} catch (Exception ex) {
throw new EJBException("ejbFindByPrimaryKey: " + ex.getMessage());
}
if (result) {
System.out.println("leaving ejbFindByPrimaryKey with " + employeeid);
return employeeid;
} else {
throw new ObjectNotFoundException("Row for id " + employeeid + " not
found.");
}
}
private boolean selectByPrimaryKey(Integer employeeid) {
System.out.println("EmployeeEJB.selectByPrimaryKey: enter, employeeid="
+ employeeid);
try {
String sqlString = "select FIRSTNAME, LASTNAME, EXTENSION, CITY_ID,
DEPARTMENT_ID " +
"from EMPLOYEE where EMPLOYEE_ID=?";
java.sql.PreparedStatement preparedStatement =
connection.prepareStatement(sqlString);
preparedStatement.clearParameters();
preparedStatement.setObject(1, employeeid, Types.INTEGER);
preparedStatement.executeQuery();
ResultSet rs = preparedStatement.getResultSet();
if (rs.next()) {
setFirstname(rs.getString(1));
setLastname(rs.getString(2));
setExtension(rs.getString(3));
setCityid(new Integer(rs.getInt(4)));
setDepartmentid(new Integer(rs.getInt(5)));
}
rs.close();
preparedStatement.close();
} catch (SQLException e) {
System.out.println("Error fetching Employee from database");

183

code/Chapter7/Employee/EmployeeEJB/com/masslight/Employee/EmployeeEJB.java
}
System.out.println("EmployeeEJB.selectByPrimaryKey: exit");
return true;

////////////////////////////////////////////////////////////////////////////
/
public Collection ejbFindAll() {
Vector employeeKeys = new Vector();
String sqlString = "select EMPLOYEE_ID from EMPLOYEE";
try {
Statement s = connection.createStatement();
ResultSet rs = s.executeQuery(sqlString);
while (rs.next()) {
employeeKeys.addElement(new Integer(rs.getInt(1)));
}
} catch (SQLException e) {
System.out.println("An SQL Exception occurred while querying result set
of employee");
}
return employeeKeys;
}
////////////////////////////////////////////////////////////////////////////
/
public void setEntityContext(EntityContext context) {
this.context = context;
makeConnection();
}
public void unsetEntityContext() {
this.context = null;
closeConnection();
}
////////////////////////////////////////////////////////////////////////////

public void ejbActivate() {


}
////////////////////////////////////////////////////////////////////////////

184

code/Chapter7/Employee/EmployeeEJB/com/masslight/Employee/EmployeeEJB.java
/
public void ejbPassivate() {
}
////////////////////////////////////////////////////////////////////////////
/
public Integer getEmployeeid() {
return this.employeeid;
}
public String getFirstname() {
return this.firstname;
}
public String getLastname() {
return this.lastname;
}
public String getExtension() {
return this.extension;
}
public Integer getCityid() {
return this.cityid;
}
public Integer getDepartmentid() {
return this.departmentid;
}
public City getCity() {
Properties env = new Properties();
env.setProperty("java.naming.factory.initial",
"org.jnp.interfaces.NamingContextFactory");
env.setProperty("java.naming.provider.url", "localhost:1099");
env.setProperty("java.naming.factory.url.pkgs", "org.jboss.naming");
try {
InitialContext jndiContext = new InitialContext(env);
Object ref = jndiContext.lookup("City");
CityHome home = (CityHome)PortableRemoteObject.narrow(ref,
CityHome.class);
City city = home.findByPrimaryKey(getCityid());
return city;
} catch (Exception e) {
System.out.println("Error retrieving city in employee bean");
}
return null;
}
public Department getDepartment() {
Properties env = new Properties();

185

code/Chapter7/Employee/EmployeeEJB/com/masslight/Employee/EmployeeEJB.java
env.setProperty("java.naming.factory.initial",
"org.jnp.interfaces.NamingContextFactory");
env.setProperty("java.naming.provider.url", "localhost:1099");
env.setProperty("java.naming.factory.url.pkgs", "org.jboss.naming");
try {
InitialContext jndiContext = new InitialContext(env);
Object ref = jndiContext.lookup("Department");
System.out.println(ref);
DepartmentHome home = (DepartmentHome)PortableRemoteObject.narrow(
ref, DepartmentHome.class);
System.out.println(home);
Department department = home.findByPrimaryKey(getDepartmentid());
System.out.println(department);
return department;
} catch (Exception e) {
System.out.println("Error retrieving department in employee bean");
}
return null;
}
public void setEmployeeid(Integer value) {
employeeid = value;
}
public void setFirstname(String value) {
firstname = new String(value);
}
public void setLastname(String value) {
lastname = new String(value);
}
public void setExtension(String value) {
extension = new String(value);
}
public void setCityid(Integer value) {
cityid = value;
}
public void setDepartmentid(Integer value) {
departmentid = value;
}
public void setCity(City value) throws RemoteException {
try {
setCityid(value.getCityid());
} catch (RemoteException e) {
System.out.println("Error setting city in employee bean");
}
}
public void setDepartment(Department value) {

186

code/Chapter7/Employee/EmployeeEJB/com/masslight/Employee/EmployeeEJB.java

try {
setDepartmentid(value.getDepartmentid());
} catch (RemoteException e) {
System.out.println("Error setting department in department bean");
}

City EJB
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
package com.masslight.City;
import javax.ejb.EJBObject;
import java.rmi.RemoteException;
public interface City extends EJBObject {
public
public
public
public

Integer getCityid() throws RemoteException;


String getName() throws RemoteException;
void setCityid(Integer value) throws RemoteException;
void setName(String value) throws RemoteException;

code/Chapter7/Employee/EmployeeEJB/com/masslight/City/CityHome.java
package com.masslight.City;
import java.util.Collection;
import java.rmi.RemoteException;
import javax.ejb.*;

187

code/Chapter7/Employee/EmployeeEJB/com/masslight/City/CityHome.java
public interface CityHome extends EJBHome {
public City create(Integer cityid, String name) throws RemoteException,
CreateException;
public City findByPrimaryKey(Integer cityid) throws FinderException,
RemoteException;
public Collection findAll() throws FinderException, RemoteException;
}

code/Chapter7/Employee/EmployeeEJB/com/masslight/City/CityEJB.java
package com.masslight.City;
import
import
import
import
import
import

java.sql.*;
java.util.Enumeration;
java.util.Vector;
java.util.Collection;
java.util.Iterator;
javax.ejb.*;

import javax.sql.*;
import javax.naming.*;
public class CityEJB implements EntityBean {
private EntityContext context = null;
private Connection connection = null;
private Integer cityid = new Integer(0);
private String name = new String();
////////////////////////////////////////////////////////////////////////////
/
public void makeConnection() {
try {
System.setProperty("java.naming.factory.initial",
"org.jnp.interfaces.NamingContextFactory");
System.setProperty("java.naming.provider.url", "localhost:1099");
InitialContext ic = new InitialContext();
if(ic == null) throw new Exception("initial context is null");
DataSource ds = (DataSource) ic.lookup("java:OpenBasePool");
if(ds != null) {
connection = ds.getConnection();
} else {
throw new Exception("datasource is null");

188

code/Chapter7/Employee/EmployeeEJB/com/masslight/City/CityEJB.java
}
} catch(javax.naming.NamingException exc) {
System.out.println("naming exception: "+exc.getMessage());
} catch(java.sql.SQLException ex) {
System.out.println("sql exception: "+ex.getMessage());
} catch(Exception except) {
System.out.println(except.getMessage());
}
}
////////////////////////////////////////////////////////////////////////////

public void closeConnection() {


try {
connection.close();
} catch (SQLException e) {
System.out.println("An SQL Exception occurred while closing the
connection");
}
}
////////////////////////////////////////////////////////////////////////////

public Integer ejbCreate(Integer cityid, String name) throws CreateException


{

try {
insertRow(cityid, name);
} catch (Exception ex) {
throw new EJBException("ejbCreate: " + ex.getMessage());
}
setCityid(cityid);
setName(name);
return cityid;

public void ejbPostCreate(Integer cityid, String name) {


}
private void insertRow(Integer cityid, String name) {
String sqlString = "insert into CITY (CITY_ID, NAME) values (";
sqlString += cityid;
sqlString += ", '" + name;
sqlString += "')";
try {
connection.setAutoCommit(false);
Statement s = connection.createStatement();
s.executeUpdate(sqlString);
connection.commit();
s.close();

189

code/Chapter7/Employee/EmployeeEJB/com/masslight/City/CityEJB.java
} catch (SQLException e) {
try {
connection.rollback();
} catch (SQLException e2) {
System.out.println("Big problems, error committing, error rolling
back!!!");
}
System.out.println("Error inserting City into database");
}
}
////////////////////////////////////////////////////////////////////////////

public void ejbRemove() {


try {
deleteRow(getCityid());
} catch (Exception ex) {
throw new EJBException("ejbRemove: " + ex.getMessage());
}
}
private void deleteRow(Integer cityid) {
String sqlString = "delete from CITY where CITY_ID = ";
sqlString += cityid;
try {
connection.setAutoCommit(false);
Statement s = connection.createStatement();
s.executeUpdate(sqlString);
connection.commit();
s.close();
} catch (SQLException e) {
try {
connection.rollback();
} catch (SQLException e2) {
System.out.println("Big problems, error committing, error rolling
back!!!");
}
System.out.println("Error deleting City from database");
}
}
////////////////////////////////////////////////////////////////////////////

public void ejbLoad() {


setCityid((Integer)(context.getPrimaryKey()));

190

code/Chapter7/Employee/EmployeeEJB/com/masslight/City/CityEJB.java

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

private void loadRow() {


try {
String sqlString = "select NAME from CITY where CITY_ID=";
sqlString += getCityid();
Statement s = connection.createStatement();
s.executeQuery(sqlString);
ResultSet rs = s.getResultSet();
if (rs.next()) {
setName(rs.getString(1));
}
s.close();
} catch (SQLException e) {
System.out.println("Error fetching City from database");
}
}
////////////////////////////////////////////////////////////////////////////
/
public void ejbStore() {
setCityid((Integer)(context.getPrimaryKey()));
try {
storeRow();
} catch (Exception ex) {
throw new EJBException("ejbLoad: " + ex.getMessage());
}
}
private void storeRow() {
String sqlString = "update CITY set NAME = '";
sqlString += getName();
sqlString += "'";
sqlString += " where CITY_ID = ";
sqlString += getCityid();
try {
connection.setAutoCommit(false);
Statement s = connection.createStatement();
s.executeUpdate(sqlString);
connection.commit();
s.close();
} catch (SQLException e) {

191

code/Chapter7/Employee/EmployeeEJB/com/masslight/City/CityEJB.java
try {
connection.rollback();
} catch (SQLException e2) {
System.out.println("Big problems, error committing, error rolling
back!!!");
}
System.out.println("Error updating City in database");
}
}
////////////////////////////////////////////////////////////////////////////
/
public Integer ejbFindByPrimaryKey(Integer cityid) throws FinderException {
boolean result;
try {
result = selectByPrimaryKey(cityid);
} catch (Exception ex) {
throw new EJBException("ejbFindByPrimaryKey: " + ex.getMessage());
}
if (result) {
System.out.println("leaving ejbFindByPrimaryKey with " + cityid);
return cityid;
} else {
throw new ObjectNotFoundException("Row for id " + cityid + " not
found.");
}
}
private boolean selectByPrimaryKey(Integer cityid) {
try {
String sqlString = "select NAME from CITY where CITY_ID=";
sqlString += cityid;
Statement s = connection.createStatement();
s.executeQuery(sqlString);
ResultSet rs = s.getResultSet();
if (rs.next()) {
setName(rs.getString(1));
}
s.close();
} catch (SQLException e) {
System.out.println("Error fetching City from database");
}
return true;
}

192

code/Chapter7/Employee/EmployeeEJB/com/masslight/City/CityEJB.java
////////////////////////////////////////////////////////////////////////////
/
public Collection ejbFindAll() {
Vector cityKeys = new Vector();
String sqlString = "select CITY_ID from CITY";
try {
Statement s = connection.createStatement();
ResultSet rs = s.executeQuery(sqlString);
while (rs.next()) {
cityKeys.addElement(new Integer(rs.getInt(1)));
}
} catch (SQLException e) {
System.out.println("An SQL Exception occurred while querying result set
of city");
}
return cityKeys;
}
////////////////////////////////////////////////////////////////////////////
/
public void setEntityContext(EntityContext context) {
this.context = context;
makeConnection();
}
public void unsetEntityContext() {
this.context = null;
closeConnection();
}
////////////////////////////////////////////////////////////////////////////

public void ejbActivate() {


}
////////////////////////////////////////////////////////////////////////////

public void ejbPassivate() {


}

193

code/Chapter7/Employee/EmployeeEJB/com/masslight/City/CityEJB.java
////////////////////////////////////////////////////////////////////////////
/
public Integer getCityid() {
return this.cityid;
}
public String getName() {
return this.name;
}
public void setCityid(Integer value) {
cityid = value;
}
public void setName(String value) {
name = new String(value);
}
}

Department EJB
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
package com.masslight.Department;
import javax.ejb.EJBObject;
import java.rmi.RemoteException;
public interface Department extends EJBObject {
public Integer getDepartmentid() throws RemoteException;

194

code/Chapter7/Employee/EmployeeEJB/com/masslight/Department/Department.java
public
public
public
public
public

String getName() throws RemoteException;


String getPhone() throws RemoteException;
void setDepartmentid(Integer value) throws RemoteException;
void setName(String value) throws RemoteException;
void setPhone(String value) throws RemoteException;

code/Chapter7/Employee/EmployeeEJB/com/masslight/Department/DepartmentHome.java
package com.masslight.Department;
import java.util.Collection;
import java.rmi.RemoteException;
import javax.ejb.*;
public interface DepartmentHome extends EJBHome {
public Department create(Integer departmentid,
String name,
String phone)
throws RemoteException, CreateException;
public Department findByPrimaryKey(Integer departmentid)
throws FinderException, RemoteException;
public Collection findAll()
throws FinderException, RemoteException;
}

code/Chapter7/Employee/EmployeeEJB/com/masslight/Department/DepartmentEJB.java
package com.masslight.Department;
import
import
import
import
import
import

java.sql.*;
java.util.Enumeration;
java.util.Vector;
java.util.Collection;
java.util.Iterator;
javax.ejb.*;

import javax.sql.*;
import javax.naming.*;
public class DepartmentEJB implements EntityBean {
private EntityContext context = null;

195

code/Chapter7/Employee/EmployeeEJB/com/masslight/Department/DepartmentEJB.java
private Connection connection = null;
private Integer departmentid = new Integer(0);
private String name = new String();
private String phone = new String();
////////////////////////////////////////////////////////////////////////////

public void makeConnection() {


try {
System.setProperty("java.naming.factory.initial",
"org.jnp.interfaces.NamingContextFactory");
System.setProperty("java.naming.provider.url", "localhost:1099");
InitialContext ic = new InitialContext();
if(ic == null) throw new Exception("initial context is null");
DataSource ds = (DataSource) ic.lookup("java:OpenBasePool");
if(ds != null) {
connection = ds.getConnection();
} else {
throw new Exception("datasource is null");
}
} catch(javax.naming.NamingException exc) {
System.out.println("naming exception: "+exc.getMessage());
} catch(java.sql.SQLException ex) {
System.out.println("sql exception: "+ex.getMessage());
} catch(Exception except) {
System.out.println(except.getMessage());
}
}
////////////////////////////////////////////////////////////////////////////

public void closeConnection() {


try {
connection.close();
} catch (SQLException e) {
System.out.println("An SQL Exception occurred while closing the
connection");
}
}
////////////////////////////////////////////////////////////////////////////

public Integer ejbCreate(Integer departmentid,


String name,
String phone) throws CreateException {
try {
insertRow(departmentid, name, phone);
} catch (Exception ex) {

196

code/Chapter7/Employee/EmployeeEJB/com/masslight/Department/DepartmentEJB.java
throw new EJBException("ejbCreate: " + ex.getMessage());
}
setDepartmentid(departmentid);
setName(name);
setPhone(phone);
return departmentid;
}
public void ejbPostCreate(Integer departmentid, String name, String phone) {
}
private void insertRow(Integer departmentid, String name, String phone) {
String sqlString = "insert into DEPARTMENT (DEPARTMENT_ID, NAME, PHONE)
values (";
sqlString += departmentid;
sqlString += ", '" + name;
sqlString += "', '" + phone;
sqlString += "')";
try {
connection.setAutoCommit(false);
Statement s = connection.createStatement();
s.executeUpdate(sqlString);
connection.commit();
s.close();
} catch (SQLException e) {
try {
connection.rollback();
} catch (SQLException e2) {
System.out.println("Big problems, error committing, error rolling
back!!!");
}
System.out.println("Error inserting Department into database");
}
}
////////////////////////////////////////////////////////////////////////////

public void ejbRemove() {


try {
deleteRow(getDepartmentid());
} catch (Exception ex) {
throw new EJBException("ejbRemove: " + ex.getMessage());
}
}
private void deleteRow(Integer departmentid) {

197

code/Chapter7/Employee/EmployeeEJB/com/masslight/Department/DepartmentEJB.java
String sqlString = "delete from DEPARTMENT where DEPARTMENT_ID = ";
sqlString += departmentid;
System.out.println("using sql: " + sqlString);
try {
connection.setAutoCommit(false);
Statement s = connection.createStatement();
s.executeUpdate(sqlString);
connection.commit();
s.close();
} catch (SQLException e) {
try {
connection.rollback();
} catch (SQLException e2) {
System.out.println("Big problems, error committing, error rolling
back!!!");
}
System.out.println("Error deleting Department from database");
}
}
////////////////////////////////////////////////////////////////////////////
/
public void ejbLoad() {
setDepartmentid((Integer)(context.getPrimaryKey()));
try {
loadRow();
} catch (Exception ex) {
throw new EJBException("ejbLoad: " + ex.getMessage());
}
}
private void loadRow() {
try {
String sqlString = "select NAME, PHONE from DEPARTMENT where
DEPARTMENT_ID=";
sqlString += getDepartmentid();
Statement s = connection.createStatement();
s.executeQuery(sqlString);
ResultSet rs = s.getResultSet();
if (rs.next()) {
setName(rs.getString(1));
setPhone(rs.getString(2));
}
s.close();
} catch (SQLException e) {
System.out.println("Error fetching Department from database");
}
}

198

code/Chapter7/Employee/EmployeeEJB/com/masslight/Department/DepartmentEJB.java
////////////////////////////////////////////////////////////////////////////

public void ejbStore() {


setDepartmentid((Integer)(context.getPrimaryKey()));
try {
storeRow();
} catch (Exception ex) {
throw new EJBException("ejbLoad: " + ex.getMessage());
}
}
private void storeRow() {
String sqlString = "update DEPARTMENT set NAME = '";
sqlString += getName();
sqlString += "', PHONE='";
sqlString += getPhone();
sqlString += "' where DEPARTMENT_ID = ";
sqlString += getDepartmentid();
try {
connection.setAutoCommit(false);
Statement s = connection.createStatement();
s.executeUpdate(sqlString);
connection.commit();
s.close();
} catch (SQLException e) {
try {
connection.rollback();
} catch (SQLException e2) {
System.out.println("Big problems, error committing, error rolling
back!!!");
}
System.out.println("Error updating Department in database");
}
}
////////////////////////////////////////////////////////////////////////////

public Integer ejbFindByPrimaryKey(Integer departmentid) throws


FinderException {
boolean result;
try {
result = selectByPrimaryKey(departmentid);
} catch (Exception ex) {

199

code/Chapter7/Employee/EmployeeEJB/com/masslight/Department/DepartmentEJB.java
}

throw new EJBException("ejbFindByPrimaryKey: " + ex.getMessage());

if (result) {
System.out.println("leaving ejbFindByPrimaryKey with " + departmentid);
return departmentid;
} else {
throw new ObjectNotFoundException("Row for id " + departmentid + " not
found.");
}
}
private boolean selectByPrimaryKey(Integer departmentid) {
try {
String sqlString = "select NAME, PHONE from DEPARTMENT where
DEPARTMENT_ID=";
sqlString += departmentid;
Statement s = connection.createStatement();
s.executeQuery(sqlString);
ResultSet rs = s.getResultSet();
if (rs.next()) {
setName(rs.getString(1));
setPhone(rs.getString(2));
}
s.close();
} catch (SQLException e) {
System.out.println("Error fetching Department from database");
}
return true;
}
////////////////////////////////////////////////////////////////////////////
/
public Collection ejbFindAll() {
Vector departmentKeys = new Vector();
String sqlString = "select DEPARTMENT_ID from DEPARTMENT";
try {
Statement s = connection.createStatement();
ResultSet rs = s.executeQuery(sqlString);
while (rs.next()) {
departmentKeys.addElement(new Integer(rs.getInt(1)));
}
} catch (SQLException e) {

200

code/Chapter7/Employee/EmployeeEJB/com/masslight/Department/DepartmentEJB.java
System.out.println("An SQL Exception occurred while querying result set
of department");
}
return departmentKeys;
}
////////////////////////////////////////////////////////////////////////////

public void setEntityContext(EntityContext context) {


this.context = context;
makeConnection();
}
public void unsetEntityContext() {
this.context = null;
closeConnection();
}
////////////////////////////////////////////////////////////////////////////
/
public void ejbActivate() {
}
////////////////////////////////////////////////////////////////////////////
/
public void ejbPassivate() {
}
////////////////////////////////////////////////////////////////////////////
/
public Integer getDepartmentid() {
return this.departmentid;
}
public String getName() {
return this.name;
}
public String getPhone() {
return this.phone;
}
public void setDepartmentid(Integer value) {
departmentid = value;
}

201

code/Chapter7/Employee/EmployeeEJB/com/masslight/Department/DepartmentEJB.java
public void setName(String value) {
name = new String(value);
}
public void setPhone(String value) {
phone = new String(value);
}
}

Designing the user interface


Shown here is the big picture for the user interface.

202

JSPs
The JSPs for the application are shown below.
code/Chapter7/Employee/EmployeeClient/index.jsp
<%@
<%@
<%@
<%@
<%@

page language="java" %>


taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
taglib uri="/WEB-INF/struts-form.tld" prefix="form" %>
taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>

203

code/Chapter7/Employee/EmployeeClient/index.jsp
<html:html locale="true">
<head>
<title>
My Co - Main Menu
</title>
<html:base/>
</head>
<body bgcolor="white">
<h3>
My Co - Main Menu
</h3>
<html:errors/>
<hr/>
<html:link href="employeeview.do">Employee Directory</html:link>
<br/>
<html:link href="employeeaddsetup.do">Add an Employee</html:link>
<br/>
<hr/>
</body>
</html:html>

code/Chapter7/Employee/EmployeeClient/employeeviewsuccess.jsp
<%@
<%@
<%@
<%@
<%@

page language="java" %>


taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
taglib uri="/WEB-INF/struts-form.tld" prefix="form" %>
taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>

<html:html locale="true">
<head>
<title>
My Co - Employee Directory
</title>
<html:base/>
</head>
<body bgcolor="white">
<h3>
My Co - Employee Directory
</h3>
<html:errors/>
<hr/>
<table border="1">
<tr>
<th>
Employee ID
</th>
<th>
First Name

204

code/Chapter7/Employee/EmployeeClient/employeeviewsuccess.jsp
</th>
<th>
Last Name
</th>
<th>
Extension
</th>
<th>
Department
</th>
<th>
City
</th>
<th>
</th>
<th>
</th>
</tr>
<logic:present name="employees">
<logic:iterate id="currentEmployee" name="employees"
scope="session">
<tr>
<td>
<bean:write name="currentEmployee" property="employeeid"/>
</td>
<td>
<bean:write name="currentEmployee" property="firstname"/>
</td>
<td>
<bean:write name="currentEmployee" property="lastname"/>
</td>
<td>
<bean:write name="currentEmployee" property="extension"/>
</td>
<td>
<bean:write name="currentEmployee" property="department.name"/>
</td>
<td>
<bean:write name="currentEmployee" property="city.name"/>
</td>
<td>
<a href="employeedelete.do?id=<bean:write
name="currentEmployee"
property="employeeid"/>">Delete</a>
</td>
<td>
<a href="employeemodifysetup.do?id=<bean:write
name="currentEmployee"
property="employeeid"/>">Modify</a>
</td>
</tr>
</logic:iterate>
</logic:present>

205

code/Chapter7/Employee/EmployeeClient/employeeviewsuccess.jsp
</table>
<hr/>
<html:link href="index.jsp">
Return to My Co - Main Menu
</html:link>
</body>
</html:html>

code/Chapter7/Employee/EmployeeClient/employeeviewfailure.jsp
<%@
<%@
<%@
<%@
<%@

page language="java" %>


taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
taglib uri="/WEB-INF/struts-form.tld" prefix="form" %>
taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>

<html:html locale="true">
<head>
<title>
Failure Viewing Employee
</title>
<html:base/>
</head>
<body bgcolor="white">
<h3>
Failure Viewing Employee
</h3>
<html:errors/>
<hr/>
<hr/>
<html:link href="index.jsp">
Return to My Co - Main Menu
</html:link>
</body>
</html:html>

code/Chapter7/Employee/EmployeeClient/employeeadd.jsp
<%@
<%@
<%@
<%@
<%@

page language="java" %>


taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
taglib uri="/WEB-INF/struts-form.tld" prefix="form" %>
taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>

<html:html locale="true">

206

code/Chapter7/Employee/EmployeeClient/employeeadd.jsp
<head>
<title>
Add an employee
</title>
<html:base/>
</head>
<body bgcolor="white">
<h3>
Add an employee
</h3>
<html:errors/>
<hr/>
<html:form action="/employeeadd">
<table>
<tr>
<td>
Employee ID
</td>
<td>
<html:text property="employeeid" size="5" maxlength="5"/>
</td>
</tr>
<tr>
<td>
First Name
</td>
<td>
<html:text property="firstname" size="20" maxlength="20"/>
</td>
</tr>
<tr>
<td>
Last Name
</td>
<td>
<html:text property="lastname" size="20" maxlength="20"/>
</td>
</tr>
<tr>
<td>
Extension
</td>
<td>
<html:text property="extension" size="4" maxlength="4"/>
</td>
</tr>
<tr>
<td>
City
</td>
<td>
<html:select property="cityid">
<html:options collection="cities"

207

code/Chapter7/Employee/EmployeeClient/employeeadd.jsp
name="employee" property="cityid" labelProperty="name"/>
</html:select>
</td>
</tr>
<tr>
<td>
Department
</td>
<td>
<html:select property="departmentid">
<html:options collection="departments"
name="employee" property="departmentid" labelProperty="name"/>
</html:select>
</td>
</tr>
</table>
<html:submit>
Add
</html:submit>
<html:reset>
Reset
</html:reset>
</html:form>
<hr/>
<html:link href="index.jsp">
Return to My Co - Main Menu
</html:link>
</body>
</html:html>

code/Chapter7/Employee/EmployeeClient/employeeaddsuccess.jsp
<%@
<%@
<%@
<%@
<%@

page language="java" %>


taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
taglib uri="/WEB-INF/struts-form.tld" prefix="form" %>
taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>

<html:html locale="true">
<head>
<title>
Employee Added Successfully
</title>
<html:base/>
</head>
<body bgcolor="white">
<h3>
Employee Added Successfully
</h3>
<html:errors/>

208

code/Chapter7/Employee/EmployeeClient/employeeaddsuccess.jsp
<hr/>
<hr/>
<html:link href="index.jsp">
Return to My Co - Main Menu
</html:link>
</body>
</html:html>

code/Chapter7/Employee/EmployeeClient/employeeaddfailure.jsp
<%@
<%@
<%@
<%@
<%@

page language="java" %>


taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
taglib uri="/WEB-INF/struts-form.tld" prefix="form" %>
taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>

<html:html locale="true">
<head>
<title>
Failure Adding Employee
</title>
<html:base/>
</head>
<body bgcolor="white">
<h3>
Failure Adding Employee
</h3>
<html:errors/>
<hr/>
<hr/>
<html:link href="index.jsp">
Return to My Co - Main Menu
</html:link>
</body>
</html:html>

code/Chapter7/Employee/EmployeeClient/employeedeletesuccess.jsp
<%@
<%@
<%@
<%@
<%@

page language="java" %>


taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
taglib uri="/WEB-INF/struts-form.tld" prefix="form" %>
taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>

<html:html locale="true">
<head>
<title>

209

code/Chapter7/Employee/EmployeeClient/employeedeletesuccess.jsp
Employee Deleted Successfully
</title>
<html:base/>
</head>
<body bgcolor="white">
<h3>
Employee Deleted Successfully
</h3>
<html:errors/>
<hr/>
<hr/>
<html:link href="index.jsp">
Return to My Co - Main Menu
</html:link>
</body>
</html:html>

code/Chapter7/Employee/EmployeeClient/employeedeletefailure.jsp
<%@
<%@
<%@
<%@
<%@

page language="java" %>


taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
taglib uri="/WEB-INF/struts-form.tld" prefix="form" %>
taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>

<html:html locale="true">
<head>
<title>
Failure Deleting Employee
</title>
<html:base/>
</head>
<body bgcolor="white">
<h3>
Failure Deleting Employee
</h3>
<html:errors/>
<hr/>
<hr/>
<html:link href="index.jsp">
Return to My Co - Main Menu
</html:link>
</body>
</html:html>

210

code/Chapter7/Employee/EmployeeClient/employeemodify.jsp
<%@
<%@
<%@
<%@
<%@

page language="java" %>


taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
taglib uri="/WEB-INF/struts-form.tld" prefix="form" %>
taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>

<html:html locale="true">
<head>
<title>
Modify employee
</title>
<html:base/>
</head>
<body bgcolor="white">
<h3>
Modify employee
</h3>
<html:errors/>
<hr/>
<html:form action="/employeemodify">
<html:hidden name="employee" property="employeeid"/>
<table>
<tr>
<td>
First Name
</td>
<td>
<html:text name="employee" property="firstname" size="20"
maxlength="20"/>
</td>
</tr>
<tr>
<td>
Last Name
</td>
<td>
<html:text name="employee" property="lastname" size="20"
maxlength="20"/>
</td>
</tr>
<tr>
<td>
Extension
</td>
<td>
<html:text name="employee" property="extension" size="4"
maxlength="4"/>
</td>
</tr>
<tr>
<td>
City
</td>

211

code/Chapter7/Employee/EmployeeClient/employeemodify.jsp
<td>
<html:select name="employee" property="cityid">
<html:options collection="cities"
name="employee" property="cityid" labelProperty="name"/>
</html:select>
</td>
</tr>
<tr>
<td>
Department
</td>
<td>
<html:select name="employee" property="departmentid">
<html:options collection="departments"
name="employee" property="departmentid" labelProperty="name"/>
</html:select>
</td>
</tr>
</table>
<html:submit>
Modify
</html:submit>
<html:reset>
Reset
</html:reset>
</html:form>
<hr/>
<html:link href="index.jsp">
Return to My Co - Main Menu
</html:link>
</body>
</html:html>

ApplicationResources.properties
Create the ApplicationResources.properties file under the classes directory.
code/Chapter7/Employee/EmployeeClient/WEBINF/ApplicationResources.properties
employee.employeeidrequired=Employee ID is required<BR>
employee.employeeidalreadyused=Empoyee ID is not available<BR>
employee.lastnamerequired=Last name is required<BR>
employee.firstnamerequired=First name is required<BR>
errors.header=<font color="red"><HR>Validation Error</HR></font><UL>
errors.footer=</UL>

212
The Action and Form Classes
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/WEBINF/classes/com/masslight/Employee/EmployeeAddAction.java
package com.masslight.Employee;
import com.masslight.City.City;
import com.masslight.City.CityHome;
import com.masslight.Department.Department;
import com.masslight.Department.DepartmentHome;
import
import
import
import
import
import
import
import
import

java.util.Vector;
java.io.IOException;
java.lang.reflect.InvocationTargetException;
java.util.Locale;
java.util.Hashtable;
javax.servlet.*;
javax.servlet.http.*;
org.apache.struts.action.*;
org.apache.struts.util.*;

import java.util.Properties;
import javax.rmi.PortableRemoteObject;
import javax.naming.*;
public final class EmployeeAddAction extends Action {
public EmployeeAddAction() {
}
public ActionForward perform(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
Locale locale = getLocale(request);
MessageResources messages = getResources();
HttpSession session = request.getSession();
EmployeeAddForm employeeAddForm = (EmployeeAddForm)form;
ActionErrors errors = new ActionErrors();
// insert logic here
Properties env = new Properties();
env.setProperty("java.naming.factory.initial",
"org.jnp.interfaces.NamingContextFactory");

213

code/Chapter7/Employee/EmployeeClient/WEBINF/classes/com/masslight/Employee/EmployeeAddAction.java
env.setProperty("java.naming.provider.url", "localhost:1099");
env.setProperty("java.naming.factory.url.pkgs", "org.jboss.naming");
try {
InitialContext jndiContext = new InitialContext(env);
Object ref = jndiContext.lookup("Employee");
EmployeeHome home = (EmployeeHome)PortableRemoteObject.narrow(ref,
EmployeeHome.class);
Employee employee = home.create(employeeAddForm.getEmployeeid(),
employeeAddForm.getFirstname(),
employeeAddForm.getLastname(),
employeeAddForm.getExtension(),
employeeAddForm.getCityid(),
employeeAddForm.getDepartmentid()
);
} catch (Exception e) {
return (mapping.findForward("employeeaddfailure"));
}
if (mapping.getAttribute() != null) {
if ("request".equals(mapping.getScope()))
request.removeAttribute(mapping.getAttribute());
else
session.removeAttribute(mapping.getAttribute());
}
return (mapping.findForward("employeeaddsuccess"));
}
}

code/Chapter7/Employee/EmployeeClient/WEBINF/classes/com/masslight/Employee/EmployeeAddForm.java
package com.masslight.Employee;
import com.masslight.City.City;
import com.masslight.City.CityHome;
import com.masslight.Department.Department;
import com.masslight.Department.DepartmentHome;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts.action.*;

214

code/Chapter7/Employee/EmployeeClient/WEBINF/classes/com/masslight/Employee/EmployeeAddForm.java
public class EmployeeAddForm extends ActionForm {
private
private
private
private
private
private

Integer employeeid = new Integer(0);


String firstname = new String();
String lastname = new String();
String extension = new String();
Integer cityid = new Integer(0);
Integer departmentid = new Integer(0);

public EmployeeAddForm() {
}
public void reset(ActionMapping mapping, HttpServletRequest request) {
}
public ActionErrors validate(ActionMapping mapping, HttpServletRequest
request) {
ActionErrors errors = new ActionErrors();
// validate fields here
/*
try {
if (getEmployeeid().intValue() == 0) {
errors.add("employeeid", new
ActionError("employee.employeeidrequired"));
}
Employee employee = new Employee();
employee.setEmployeeid(getEmployeeid());
if (employee.fetch() != false) {
errors.add("employeeid", new
ActionError("employee.employeeidalreadyused"));
}
} catch (Exception e) {
errors.add("employeeid", new
ActionError("employee.employeeidrequired"));
}
if (getFirstname() == null || getFirstname().length() < 1) {
errors.add("firstname", new
ActionError("employee.firstnamerequired"));
}
if (getLastname() == null || getLastname().length() < 1) {
errors.add("lastname", new
ActionError("employee.lastnamerequired"));
}
*/
return errors;

215

code/Chapter7/Employee/EmployeeClient/WEBINF/classes/com/masslight/Employee/EmployeeAddForm.java
}
public
public
public
public
public
public

Integer getEmployeeid() { return employeeid; }


void setEmployeeid(Integer value) { employeeid = value; }
String getLastname() { return lastname; }
void setLastname(String value) { lastname = new String(value); }
String getFirstname() { return firstname; }
void setFirstname(String value) { firstname = new String(value);

public String getExtension() { return extension; }


public void setExtension(String value) { extension = new String(value);

public
public
public
public

Integer getDepartmentid() { return departmentid; }


void setDepartmentid(Integer value) { departmentid = value; }
Integer getCityid() { return cityid; }
void setCityid(Integer value) { cityid = value; }

code/Chapter7/Employee/EmployeeClient/WEBINF/classes/com/masslight/Employee/EmployeeAddSetupAction.java
package com.masslight.Employee;
import com.masslight.City.City;
import com.masslight.City.CityHome;
import com.masslight.Department.Department;
import com.masslight.Department.DepartmentHome;
import
import
import
import
import
import
import
import
import
import
import
import
import

java.util.Vector;
java.io.IOException;
java.lang.reflect.InvocationTargetException;
java.util.Locale;
java.util.Hashtable;
java.util.Collection;
java.util.Properties;
javax.servlet.*;
javax.servlet.http.*;
org.apache.struts.action.*;
org.apache.struts.util.*;
javax.rmi.PortableRemoteObject;
javax.naming.*;

public final class EmployeeAddSetupAction extends Action {


public EmployeeAddSetupAction() {
}

216

code/Chapter7/Employee/EmployeeClient/WEBINF/classes/com/masslight/Employee/EmployeeAddSetupAction.java
public ActionForward perform(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
Locale locale = getLocale(request);
MessageResources messages = getResources();
HttpSession session = request.getSession();
ActionErrors errors = new ActionErrors();
// insert logic here
Properties env = new Properties();
env.setProperty("java.naming.factory.initial",
"org.jnp.interfaces.NamingContextFactory");
env.setProperty("java.naming.provider.url", "localhost:1099");
env.setProperty("java.naming.factory.url.pkgs", "org.jboss.naming");
try {
InitialContext jndiContext = new InitialContext(env);
Object ref = jndiContext.lookup("City");
CityHome home = (CityHome)PortableRemoteObject.narrow(ref,
CityHome.class);
Collection cities = home.findAll();
session.setAttribute("cities", cities);
} catch (Exception e1) {
return (mapping.findForward("employeeaddsetupfailure"));
}
try {
InitialContext jndiContext = new InitialContext(env);
Object ref = jndiContext.lookup("Department");
DepartmentHome home = (DepartmentHome)PortableRemoteObject.narrow(
ref, DepartmentHome.class);
Collection departments = home.findAll();
session.setAttribute("departments", departments);
} catch (Exception e2) {
return (mapping.findForward("employeeaddsetupfailure"));
}
if (mapping.getAttribute() != null) {
if ("request".equals(mapping.getScope()))
request.removeAttribute(mapping.getAttribute());
else
session.removeAttribute(mapping.getAttribute());
}

217

code/Chapter7/Employee/EmployeeClient/WEBINF/classes/com/masslight/Employee/EmployeeAddSetupAction.java
return (mapping.findForward("employeeaddsetupsuccess"));
}
}

code/Chapter7/Employee/EmployeeClient/WEBINF/classes/com/masslight/Employee/EmployeeDeleteAction.java
package com.masslight.Employee;
import com.masslight.City.City;
import com.masslight.City.CityHome;
import com.masslight.Department.Department;
import com.masslight.Department.DepartmentHome;
import
import
import
import
import
import
import
import
import
import
import
import

java.util.Vector;
java.io.IOException;
java.lang.reflect.InvocationTargetException;
java.util.Locale;
java.util.Hashtable;
java.util.Properties;
javax.servlet.*;
javax.servlet.http.*;
org.apache.struts.action.*;
org.apache.struts.util.*;
javax.rmi.PortableRemoteObject;
javax.naming.*;

public final class EmployeeDeleteAction extends Action {


public EmployeeDeleteAction() {
}
public ActionForward perform(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
Locale locale = getLocale(request);
MessageResources messages = getResources();
HttpSession session = request.getSession();
EmployeeDeleteForm employeeDeleteForm = (EmployeeDeleteForm)form;
ActionErrors errors = new ActionErrors();

218

code/Chapter7/Employee/EmployeeClient/WEBINF/classes/com/masslight/Employee/EmployeeDeleteAction.java
// insert logic here
Properties env = new Properties();
env.setProperty("java.naming.factory.initial",
"org.jnp.interfaces.NamingContextFactory");
env.setProperty("java.naming.provider.url", "localhost:1099");
env.setProperty("java.naming.factory.url.pkgs", "org.jboss.naming");
try {
InitialContext jndiContext = new InitialContext(env);
Object ref = jndiContext.lookup("Employee");
EmployeeHome home = (EmployeeHome)PortableRemoteObject.narrow(ref,
EmployeeHome.class);
Employee employee =
home.findByPrimaryKey(employeeDeleteForm.getId());
employee.remove();
} catch (Exception e) {
return (mapping.findForward("employeedeletefailure"));
}
if (mapping.getAttribute() != null) {
if ("request".equals(mapping.getScope()))
request.removeAttribute(mapping.getAttribute());
else
session.removeAttribute(mapping.getAttribute());
}
return (mapping.findForward("employeedeletesuccess"));
}
}

code/Chapter7/Employee/EmployeeClient/WEBINF/classes/com/masslight/Employee/EmployeeDeleteForm.java
package com.masslight.Employee;
import com.masslight.City.City;
import com.masslight.City.CityHome;
import com.masslight.Department.Department;
import com.masslight.Department.DepartmentHome;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts.action.*;

219

code/Chapter7/Employee/EmployeeClient/WEBINF/classes/com/masslight/Employee/EmployeeDeleteForm.java
public class EmployeeDeleteForm extends ActionForm {
private Integer id;
public EmployeeDeleteForm() {
}
public Integer getId() { return id; }
public void setId(Integer value) { id = value; }
public void reset(ActionMapping mapping, HttpServletRequest request) {
}
public ActionErrors validate(ActionMapping mapping, HttpServletRequest
request) {
ActionErrors errors = new ActionErrors();
// validate fields here
}

return errors;

code/Chapter7/Employee/EmployeeClient/WEBINF/classes/com/masslight/Employee/EmployeeModifyAction.java
package com.masslight.Employee;
import com.masslight.City.City;
import com.masslight.City.CityHome;
import com.masslight.Department.Department;
import com.masslight.Department.DepartmentHome;
import
import
import
import
import
import
import
import
import
import
import
import

java.util.Vector;
java.io.IOException;
java.lang.reflect.InvocationTargetException;
java.util.Locale;
java.util.Hashtable;
javax.servlet.*;
javax.servlet.http.*;
org.apache.struts.action.*;
org.apache.struts.util.*;
java.util.Properties;
javax.rmi.PortableRemoteObject;
javax.naming.*;

220

code/Chapter7/Employee/EmployeeClient/WEBINF/classes/com/masslight/Employee/EmployeeModifyAction.java
public final class EmployeeModifyAction extends Action {
public EmployeeModifyAction() {
}
public ActionForward perform(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
Locale locale = getLocale(request);
MessageResources messages = getResources();
HttpSession session = request.getSession();
EmployeeModifyForm employeeModifyForm = (EmployeeModifyForm)form;
ActionErrors errors = new ActionErrors();
// insert logic here
Properties env = new Properties();
env.setProperty("java.naming.factory.initial",
"org.jnp.interfaces.NamingContextFactory");
env.setProperty("java.naming.provider.url", "localhost:1099");
env.setProperty("java.naming.factory.url.pkgs", "org.jboss.naming");
try {
InitialContext jndiContext = new InitialContext(env);
Object ref = jndiContext.lookup("Employee");
EmployeeHome home = (EmployeeHome)PortableRemoteObject.narrow(ref,
EmployeeHome.class);
Employee employee =
home.findByPrimaryKey(employeeModifyForm.getEmployeeid());
employee.setFirstname(employeeModifyForm.getFirstname());
employee.setLastname(employeeModifyForm.getLastname());
employee.setExtension(employeeModifyForm.getExtension());
employee.setCityid(employeeModifyForm.getCityid());
employee.setDepartmentid(employeeModifyForm.getDepartmentid());
} catch (Exception e) {
return (mapping.findForward("employeemodifyfailure"));
}
if (mapping.getAttribute() != null) {
if ("request".equals(mapping.getScope()))
request.removeAttribute(mapping.getAttribute());
else
session.removeAttribute(mapping.getAttribute());
}

221

code/Chapter7/Employee/EmployeeClient/WEBINF/classes/com/masslight/Employee/EmployeeModifyAction.java
return (mapping.findForward("employeemodifysuccess"));
}
}

code/Chapter7/Employee/EmployeeClient/WEBINF/classes/com/masslight/Employee/EmployeeModifyForm.java
package com.masslight.Employee;
import com.masslight.City.City;
import com.masslight.City.CityHome;
import com.masslight.Department.Department;
import com.masslight.Department.DepartmentHome;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts.action.*;
public class EmployeeModifyForm extends ActionForm {
private
private
private
private
private
private

Integer employeeid = new Integer(0);


String firstname = new String();
String lastname = new String();
String extension = new String();
Integer cityid = new Integer(0);
Integer departmentid = new Integer(0);

public EmployeeModifyForm() {
}
public void reset(ActionMapping mapping, HttpServletRequest request) {
}
public ActionErrors validate(ActionMapping mapping, HttpServletRequest
request) {
ActionErrors errors = new ActionErrors();
/*
// validate fields here
try {
if (getEmployeeid().intValue() == 0) {
errors.add("employeeid", new
ActionError("employee.employeeidrequired"));
}
} catch (Exception e) {

222

code/Chapter7/Employee/EmployeeClient/WEBINF/classes/com/masslight/Employee/EmployeeModifyForm.java
errors.add("employeeid", new
ActionError("employee.employeeidrequired"));
}
if (getFirstname() == null || getFirstname().length() < 1) {
errors.add("firstname", new
ActionError("employee.firstnamerequired"));
}
if (getLastname() == null || getLastname().length() < 1) {
errors.add("lastname", new
ActionError("employee.lastnamerequired"));
}
*/
return errors;
}
public
public
public
public
public
public

Integer getEmployeeid() { return employeeid; }


void setEmployeeid(Integer value) { employeeid = value; }
String getLastname() { return lastname; }
void setLastname(String value) { lastname = new String(value); }
String getFirstname() { return firstname; }
void setFirstname(String value) { firstname = new String(value);

public String getExtension() { return extension; }


public void setExtension(String value) { extension = new String(value);

public
public
public
public

Integer getDepartmentid() { return departmentid; }


void setDepartmentid(Integer value) { departmentid = value; }
Integer getCityid() { return cityid; }
void setCityid(Integer value) { cityid = value; }

code/Chapter7/Employee/EmployeeClient/WEBINF/classes/com/masslight/Employee/EmployeeModifySetupAction.java
package com.masslight.Employee;
import com.masslight.City.City;
import com.masslight.City.CityHome;
import com.masslight.Department.Department;
import com.masslight.Department.DepartmentHome;
import java.util.Vector;
import java.io.IOException;

223

code/Chapter7/Employee/EmployeeClient/WEBINF/classes/com/masslight/Employee/EmployeeModifySetupAction.java
import
import
import
import
import
import
import
import
import
import
import

java.lang.reflect.InvocationTargetException;
java.util.Locale;
java.util.Hashtable;
javax.servlet.*;
javax.servlet.http.*;
org.apache.struts.action.*;
org.apache.struts.util.*;
java.util.Collection;
java.util.Properties;
javax.rmi.PortableRemoteObject;
javax.naming.*;

public final class EmployeeModifySetupAction extends Action {


public EmployeeModifySetupAction() {
}
public ActionForward perform(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
Locale locale = getLocale(request);
MessageResources messages = getResources();
HttpSession session = request.getSession();
EmployeeModifySetupForm employeeModifySetupForm =
(EmployeeModifySetupForm)form;
ActionErrors errors = new ActionErrors();
// insert logic here
Properties env = new Properties();
env.setProperty("java.naming.factory.initial",
"org.jnp.interfaces.NamingContextFactory");
env.setProperty("java.naming.provider.url", "localhost:1099");
env.setProperty("java.naming.factory.url.pkgs", "org.jboss.naming");
try {
InitialContext jndiContext = new InitialContext(env);
Object ref = jndiContext.lookup("City");
CityHome home = (CityHome)PortableRemoteObject.narrow(ref,
CityHome.class);
Collection cities = home.findAll();
session.setAttribute("cities", cities);
} catch (Exception e1) {
return (mapping.findForward("employeemodifysetupfailure"));
}
try {

224

code/Chapter7/Employee/EmployeeClient/WEBINF/classes/com/masslight/Employee/EmployeeModifySetupAction.java
InitialContext jndiContext = new InitialContext(env);
Object ref = jndiContext.lookup("Department");
DepartmentHome home = (DepartmentHome)PortableRemoteObject.narrow(
ref, DepartmentHome.class);
Collection departments = home.findAll();
session.setAttribute("departments", departments);
} catch (Exception e2) {
return (mapping.findForward("employeemodifysetupfailure"));
}
try {
InitialContext jndiContext = new InitialContext(env);
Object ref = jndiContext.lookup("Employee");
EmployeeHome home = (EmployeeHome)PortableRemoteObject.narrow(
ref, EmployeeHome.class);
Employee employee =
home.findByPrimaryKey(employeeModifySetupForm.getId());
session.setAttribute("employee", employee);
} catch (Exception e3) {
return (mapping.findForward("employeemodifysetupfailure"));
}
if (mapping.getAttribute() != null) {
if ("request".equals(mapping.getScope()))
request.removeAttribute(mapping.getAttribute());
else
session.removeAttribute(mapping.getAttribute());
}
return (mapping.findForward("employeemodifysetupsuccess"));
}
}

code/Chapter7/Employee/EmployeeClient/WEBINF/classes/com/masslight/Employee/EmployeeModifySetupForm.java
package com.masslight.Employee;
import com.masslight.City.City;
import com.masslight.City.CityHome;
import com.masslight.Department.Department;
import com.masslight.Department.DepartmentHome;

225

code/Chapter7/Employee/EmployeeClient/WEBINF/classes/com/masslight/Employee/EmployeeModifySetupForm.java
import javax.servlet.http.HttpServletRequest;
import org.apache.struts.action.*;
public class EmployeeModifySetupForm extends ActionForm {
private Integer id;
public EmployeeModifySetupForm() {
}
public Integer getId() { return id; }
public void setId(Integer value) { id = value; }
public void reset(ActionMapping mapping, HttpServletRequest request) {
}
public ActionErrors validate(ActionMapping mapping, HttpServletRequest
request) {
ActionErrors errors = new ActionErrors();
// validate fields here
}

return errors;

code/Chapter7/Employee/EmployeeClient/WEBINF/classes/com/masslight/Employee/EmployeeViewAction.java
package com.masslight.Employee;
import com.masslight.City.*;
import com.masslight.Department.*;
import
import
import
import
import
import
import
import
import
import
import
import

java.util.Vector;
java.io.IOException;
java.lang.reflect.InvocationTargetException;
java.util.Locale;
java.util.Hashtable;
javax.servlet.*;
javax.servlet.http.*;
org.apache.struts.action.*;
org.apache.struts.util.*;
java.util.Properties;
javax.rmi.PortableRemoteObject;
javax.naming.*;

226

code/Chapter7/Employee/EmployeeClient/WEBINF/classes/com/masslight/Employee/EmployeeViewAction.java
import java.util.Collection;
public final class EmployeeViewAction extends Action {
public EmployeeViewAction() {
}
public ActionForward perform(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
Locale locale = getLocale(request);
MessageResources messages = getResources();
HttpSession session = request.getSession();
ActionErrors errors = new ActionErrors();
// insert logic here
Properties env = new Properties();
env.setProperty("java.naming.factory.initial",
"org.jnp.interfaces.NamingContextFactory");
env.setProperty("java.naming.provider.url", "localhost:1099");
env.setProperty("java.naming.factory.url.pkgs", "org.jboss.naming");
try {
InitialContext jndiContext = new InitialContext(env);
Object ref = jndiContext.lookup("Employee");
EmployeeHome home = (EmployeeHome)PortableRemoteObject.narrow(ref,
EmployeeHome.class);
Collection employees = home.findAll();
session.setAttribute("employees", employees);
} catch (Exception e) {
return (mapping.findForward("employeeviewfailure"));
}
if (mapping.getAttribute() != null) {
if ("request".equals(mapping.getScope()))
request.removeAttribute(mapping.getAttribute());
else
session.removeAttribute(mapping.getAttribute());
}
return (mapping.findForward("employeeviewsuccess"));
}

227

code/Chapter7/Employee/EmployeeClient/WEBINF/classes/com/masslight/Employee/EmployeeViewAction.java
}

Configuring the Actions for the Application


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
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration
1.0//EN"
"http://jakarta.apache.org/struts/dtds/struts-config_1_0.dtd">
<struts-config>
<!-- ========== Form Bean Definitions
=================================== -->
<form-beans>
<form-bean name="employeeModifySetupForm"
type="com.masslight.Employee.EmployeeModifySetupForm"/>
<form-bean name="employeeModifyForm"
type="com.masslight.Employee.EmployeeModifyForm"/>
<form-bean name="employeeAddForm"
type="com.masslight.Employee.EmployeeAddForm"/>
<form-bean name="employeeDeleteForm"
type="com.masslight.Employee.EmployeeDeleteForm"/>
</form-beans>
<!-- ========== Global Forward Definitions
============================== -->
<global-forwards>
<forward name="employeeaddsetupsuccess" path="/employeeadd.jsp"/>
<forward name="employeeaddsuccess" path="/employeeaddsuccess.jsp"/>
<forward name="employeeaddfailure" path="/employeeaddfailure.jsp"/>
<forward name="employeeviewsuccess" path="/employeeviewsuccess.jsp"/>
<forward name="employeeviewfailure" path="/employeeviewfailure.jsp"/>

228

code/Chapter7/Employee/EmployeeClient/WEB-INF/struts-config.xml
<forward name="employeedeletesuccess" path="/employeeview.do"/>
<forward name="employeedeletefailure"
path="/employeedeletefailure.jsp"/>
<forward name="employeemodifysetupsuccess"
path="/employeemodify.jsp"/>
<forward name="employeemodifysuccess" path="/employeeview.do"/>
<forward name="employeemodifyfailure"
path="/employeemodifyfailure.jsp"/>
<forward name="cityviewsuccess" path="/cityviewsuccess.jsp"/>
<forward name="cityviewfailure" path="/cityviewfailure.jsp"/>
</global-forwards>
<!-- ========== Action Mapping Definitions
============================== -->
<action-mappings>
<action path="/employeeaddsetup"
type="com.masslight.Employee.EmployeeAddSetupAction"/>
<action path="/employeeadd"
type="com.masslight.Employee.EmployeeAddAction"
name="employeeAddForm" scope="request" input="/employeeadd.jsp"/>
<action path="/employeeview"
type="com.masslight.Employee.EmployeeViewAction"/>
<action path="/employeedelete"
type="com.masslight.Employee.EmployeeDeleteAction"
name="employeeDeleteForm" scope="request"
input="employeeviewsuccess.jsp"/>
<action path="/employeemodifysetup"
type="com.masslight.Employee.EmployeeModifySetupAction"
name="employeeModifySetupForm" scope="request"
input="/employeeviewsuccess.jsp"/>
<action path="/employeemodify"
type="com.masslight.Employee.EmployeeModifyAction"
name="employeeModifyForm" scope="request"
input="/employeemodify.jsp"/>
<action path="/cityview" type="com.masslight.City.CityViewAction"/>
</action-mappings>
</struts-config>

The web.xml Configuration File


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

229
http://.../employeeaddsetup.do is requested. The container knows from strutsconfig.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
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
"http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
<web-app>
<!-- Action Servlet Configuration -->
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>application</param-name>
<param-value>ApplicationResources</param-value>
</init-param>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
<param-name>debug</param-name>
<param-value>2</param-value>
</init-param>
<init-param>
<param-name>detail</param-name>
<param-value>2</param-value>
</init-param>
<init-param>
<param-name>validate</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<!-- Action Servlet Mapping -->
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<!-- The Welcome File List -->
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>

230

code/Chapter7/Employee/EmployeeClient/WEB-INF/web.xml
<!-- Application Tag Library Descriptor -->
<taglib>
<taglib-uri>/WEB-INF/application.tld</taglib-uri>
<taglib-location>/WEB-INF/app.tld</taglib-location>
</taglib>
<!-- Struts Tag Library Descriptors -->
<taglib>
<taglib-uri>/WEB-INF/struts-bean.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-bean.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>/WEB-INF/struts-html.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-html.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>/WEB-INF/struts-logic.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-logic.tld</taglib-location>
</taglib>
</web-app>

Creating the Enterprise Archive (.ear) containing the Enterprise Beans


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.

231

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.

232

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.

233

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.

234

Choose Bean-Managed Persistence and enter java.lang.Integer for the Primary Key
Class. Then continue to the next section.

235

Skip through the next several sections in the wizard, and stop at the TransactionManagement 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.

236

In the next dialog, the EJB deployment descriptor will be shown. Choose Finish.

237

The deployment tool main window shows the City EJB has been added.

238

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.

Compiling and Deploying


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.

239

code/Chapter7/Employee/EmployeeClient/build.xml
<project name="Employee" default="dist" basedir=".">
<!-- set global properties for this build -->
<property environment="env"/>
<property name="top" value="."/>
<property name="src" value="."/>
<property name="build" value="build"/>
<property name="dist" value="dist"/>
<property name="war_dir" value="${dist}/lib"/>
<property name="war_file" value="${war_dir}/Employee.war"/>
<property name="webinf" value="${top}/WEB-INF"/>
<property name="web.xml" value="${webinf}/web.xml"/>
<property name="classes" value="${webinf}/classes"/>
<property name="lib" value="${top}/WEB-INF/lib"/>
<property name="ejb.src" value="${top}/../EmployeeEJB"/>
<property name="struts.jar" value="${env.STRUTS_HOME}/lib/struts.jar"/>
<property name="servlet.jar" value="$
{env.TOMCAT_HOME}/lib/servlet.jar"/>
<property name="ejb.jar" value="${env.JBOSS_HOME}/lib/ext/jbossj2ee.jar"/>
<property name="jdbc.jar" value="${env.JBOSS_HOME}/lib/jbossjdbc_ext.jar"/>
<property name="deploy" value="${env.JBOSS_HOME}/deploy"/>
<target name="clean">
<!-- Delete our the ${build} and ${dist} directory trees -->
<delete dir="${build}"/>
<delete dir="${dist}"/>
<delete dir="${war_dir}"/>
</target>
<target name="init">
<!-- Create the build directory structure used by compile and dist
-->
<mkdir dir="${build}"/>
<mkdir dir="${dist}"/>
<mkdir dir="${war_dir}"/>
</target>
<target name="compile" depends="init">
<!-- Compile the java code from ${src} into ${build} -->
<javac
srcdir="${ejb.src}"
destdir="${build}"
classpath="${ejb.jar};${jdbc.jar}"/>
<javac
srcdir="${top}/${src}"
destdir="${build}"
classpath="${servlet.jar};${struts.jar};${ejb.jar}"/>
</target>
<target name="dist" depends="compile">
<!-- Put everything in a war file -->

240

code/Chapter7/Employee/EmployeeClient/build.xml
<war warfile="${war_file}" webxml="${web.xml}">
<!-- include all JSPs in root level, and all .properties files
anywhere -->
<fileset dir="${top}/${src}">
<include name="*.jsp"/>
<include name="**/*.properties"/>
</fileset>
<!-- include all tag libraries in WEB-INF, and all .xml config
files,
but not web.xml (that's handled separately) -->
<webinf dir="${webinf}">
<include name="*.tld"/>
<include name="*.xml"/>
<exclude name="web.xml"/>
</webinf>
<!-- include all libraries in WEB-INF/lib (like struts.jar) -->
<lib dir="${lib}"/>
<!-- include all compiled classes -->
<classes dir="${build}"/>
</war>
</target>
<target name="deploy">
<!-- Copy the war file to the JBoss deploy directory -->
<copy file="${war_file}" todir="${deploy}"/>
</target>
<target name="all" depends="clean,dist,deploy"/>
</project>

code/Chapter7/Employee/EmployeeEJB/build.xml
<project name="EmployeeEJB" default="dist" basedir=".">
<!-- set global properties for this build -->
<property environment="env"/>
<property name="top" value="."/>
<property name="src" value="."/>
<property name="metainf" value="${top}/META-INF"/>
<property name="build" value="build"/>
<property name="dist" value="dist"/>
<property name="jar_dir" value="${dist}/lib"/>
<property name="employee_jar_file" value="${jar_dir}/EmployeeEJB.jar"/>
<property name="city_jar_file" value="${jar_dir}/CityEJB.jar"/>
<property name="department_jar_file" value="$
{jar_dir}/DepartmentEJB.jar"/>

241

code/Chapter7/Employee/EmployeeEJB/build.xml
<property name="ear_file" value="${jar_dir}/Employee.ear"/>
<property name="webinf" value="${top}/WEB-INF"/>
<property name="web.xml" value="${webinf}/web.xml"/>
<property name="classes" value="${webinf}/classes"/>
<property name="lib" value="${top}/WEB-INF/lib"/>
<property name="ejb.jar" value="${env.JBOSS_HOME}/lib/ext/jbossj2ee.jar"/>
<property name="jdbc.jar" value="${env.JBOSS_HOME}/lib/jbossjdbc_ext.jar"/>
<property name="deploy" value="${env.JBOSS_HOME}/deploy"/>
<target name="clean">
<!-- Delete our the ${build} and ${dist} directory trees -->
<delete dir="${build}"/>
<delete dir="${dist}"/>
<delete dir="${jar_dir}"/>
</target>
<target name="init">
<!-- Create the build directory structure used by compile and dist
-->
<mkdir dir="${build}"/>
<mkdir dir="${dist}"/>
<mkdir dir="${jar_dir}"/>
</target>
<target name="compile" depends="init">
<!-- Compile the java code from ${src} into ${build} -->
<javac
srcdir="${top}/${src}"
destdir="${build}"
classpath="${ejb.jar}:${jdbc.jar}"/>
</target>
<target name="dist" depends="compile">
<!-- create Employee EJB jar file -->
<jar jarfile="${employee_jar_file}">
<metainf dir="${top}/com/masslight/Employee">
<include name="ejb-jar.xml"/>
</metainf>
<fileset dir="${build}">
<include name="**/Employee*.class"/>
</fileset>
</jar>
<!-- create City EJB jar file -->
<jar jarfile="${city_jar_file}">
<metainf dir="${top}/com/masslight/City">
<include name="ejb-jar.xml"/>
</metainf>
<fileset dir="${build}">
<include name="**/City*.class"/>

242

code/Chapter7/Employee/EmployeeEJB/build.xml
</fileset>
</jar>
<!-- create Department EJB jar file -->
<jar jarfile="${department_jar_file}">
<metainf dir="${top}/com/masslight/Department">
<include name="ejb-jar.xml"/>
</metainf>
<fileset dir="${build}">
<include name="**/Department*.class"/>
</fileset>
</jar>
<!-- wrap it all up in one big EAR file -->
<ear earfile="${ear_file}" appxml="${metainf}/application.xml">
<metainf dir="${metainf}">
<include name="sun-j2ee-ri.xml"/>
</metainf>
<fileset dir="${jar_dir}" includes="*.jar"/>
</ear>
</target>
<target name="deploy">
<!-- Copy the ear file to the JBoss deploy directory -->
<copy file="${ear_file}" todir="${deploy}"/>
</target>
<target name="all" depends="clean,dist,deploy"/>
</project>

code/Chapter7/Employee/build.xml
<project name="Employee" default="all" basedir=".">
<target name="all">
<ant dir="EmployeeClient" target="all"/>
<ant dir="EmployeeEJB" target="all"/>
</target>
<target name="clean">
<ant dir="EmployeeClient" target="clean"/>
<ant dir="EmployeeEJB" target="clean"/>
</target>
</project>

243

Test the Application


Go to http://localhost:8080/Employee/index.jsp to test the application. Try
viewing the employee directory, modifying an existing employee, deleting an employee,
and adding a new employee.

Exercises
Exercise 1. Login
Add a login process to the application. Create a table in the database to hold username
and password pairs and add some mock data. Create an EJB to provide lookups in the
database table for the authentication information. Add LoginAction and
LoginActionForm classes. Add logic to the LoginAction class so that a correct
username/password pair from the database sets a session bean named loggedIn. Alter the
pages to check for the loggedIn session bean and take appropriate action if it is not
present. Provide the necessary mapping information in the struts-config.xml for the
new LoginAction andLoginActionForm. Change the default welcome file to
login.jsp. Add a new forward called loginOk and associate it with index.jsp. Create
the login.jsp page which includes a form that is submitted to the correct action for
authentication. Add a new forward called login which is associated with login.jsp.
Develop a new Action,Logout, which removes the session bean loggedIn and forwards
to the forward named login.
Exercise 2. Discuss
Discuss how the problem of a large number of employee records in the database can
cause problems with viewing. Explain how this problem can be fixed.

244

Chapter 8. Enterprise Objects Framework


Object-Relational Mapping
Goals

After completing this chapter, the student will


be able to use the Enterprise Objects Framework (EOF) with
Enterprise JavaBeans.

Prerequisites

In order to complete this chapter successfully, the student must


be able to read and develop Java code.

Objectives

The objective for this chapter is to understand and use the Enterprise
Objects Framework for O/R mapping.

The problem
Imagine a database of 100 tables each with 10 columns. Now imagine developing the
EJBs for each of those tables. This reveals an apparent problem with BMP EJBs.
BMP EJB development is a lengthy process, even for very simple tables. But there are
issues with modifications to the schema of the database, performance, and overall
architecural design. There must be a way to avoid the pitfalls that accompany EJBs for
persistence.
Enter EOF.

Introducing The Enterprise Objects Framework


The Enterprise Objects Framework (EOF) turns working with databases into child's play.
EOF uses model files that describe information about the schema of the database, custom
queries, and any information necessary to make a connection to the database. The
EOModeler application is used to create these models; you can create one from scratch,
or you can tell it to reverse engineer an existing database. Once the model has been
created, the EOModeler application can automatically create the database, if it hasn't been
created, and generate the java classes.
The Java classes generated by EOModeler contain accessor and mutator methods for
every attribute that has been designated as a class attribute (most likely all of the
attributes for a table except for primary keys). Remember all those getXyz() and setXyz()
methods we created in our EJBs in the previous chapter? EOModeler handles all that
automatically.

245
Each instantiation of these classes, corresponds to a table in the database (or databases).
Coordinating these instances is a class named an EOEditingContext. An
EOEditingContext instance represents a sandbox for editing. It ensures that the data in the
application is consistent with those stored in the database.

Introducing EOModeler
EOModeler looks like this:

The entities shown on the left side of the window represent tables in the database.
Selecting an entity displays the attributes that are associated with the entity in the top
right area.
Used to add an entity (table)
Used to add a column (attribute) to the selected
entity

246
Used to add a relationship between entities
Used to create a fetch specification
Used to flatten a property (beyond the scope of
this material)
Used to browse the data in the database
Used to create the tables in the database, set up
the primary and foreign keys, and generate
SQL
Used to generate Java source for the selected
entity
Used to denote that the selected column is a
primary key for the selected entity
Used to denote that the selected column is a class
attribute; that is, the column will have accessor
and mutator methods in the generated Java source
Used for locking attributes (beyond the scope of
this material)

Using EOF
In EOF, all database work is done in something called an editing context, represented by
the EOEditingContext class. An editing context is a local working area that allows you
to fetch data from the database, make changes (including adding or deleting entities, or
modifying attributes), and then either save those changes back to the database or cancel
them. In database terms, this is like doing various SELECT statements, then opening a
transaction, doing INSERT, UPDATE, and/or DELETE statements, and doing a COMMIT. In
fact, all these things happen under the covers while you're working with the Enterprise
Objects Framework; they're simply well-hidden from you.
Fetching objects
The entire point of EOF is to abstract away all the details of the actual database. The first
step towards this goal is to be able to get data out of the database without using an actual
SQL statement, since the SQL could be database-specific. EOF does this with things
called fetch specifications.
A fetch specification is a description of a database query. You create fetch specifications
within EOModeler, and then you can use them in your Java code to fetch data into your
editing context.
Here is an example of a simple fetch specification in our employee model. We will
actually create and use this fetch specification later in this chapter.

247

Once you have a fetch specification defined in your model, you can use this to fetch data
from the database. The data ends up in your editing context as Java objects.
First, create an EOEditingContext or (if you have one) get a handle to an existing one.
Then retrieve the fetch specification with the static method fetchSpecificationNamed
of the EOFetchSpecification class:
EOFetchSpecification.fetchSpecificationNamed(
String name,
String entityName)

where name is the name of the fetch specification for the entity given by entityName.
Next, create an NSMutableDictionary and add the appropriate key-value pairs that
represent the bindings for the qualification. This can be done by invoking the method
takeValueForKey method on an instance of NSMutableDictionary.
instanceOfNSMutableDictionary.takeValueForKey(
Object someObject,
String key)

248
Then use the static method objectsWithFetchSpecificationAndBindings of the
EOUtilities class:
EOUtilities.objectsWithFetchSpecificationAndBindings(
EOEditingContext ec,
String entityName,
EOFetchSpecification fs,
NSDictionary bindings)

which will return NSArray of objects, representing the rows in the table entityName that
were qualified by the fetch specification and bindings. If there are no bindings required
by the fetch specification, null can be specified for the bindings.
To fetch all objects from the database, specify null for the fetch specification and
bindings. If no key-value pair for a binding is not present in the dictionary, EOF will
ignore the binding.
Here is an example of fetching with and without a fetch specification and bindings:
EOEditingContext ec = new EOEditingContext();
// fetch all objects from the Employee table
NSArray employees =
(NSArray)EOUtilities.objectsWithFetchSpecification(
ec, "Employee", null, null);
// display all employees
for (int count = 0; count < employees.count(); count++)
System.out.println((Employee)(employees.objectAtIndex(count)));
// ----------------------------------------// now fetch just employees in a single department
EOFetchSpecification fs = EOFetchSpecificationNamed("Employee",
"fetchByDeptId");
// set the bindings
NSMutableDictionary bindings = new NSMutableDictionary();
bindings.takeValueForKey(new Integer("1"), "deptId");
// fetch the objects
NSArray specialEmployees =
(NSArray(EOUtilities.objectsWithFetchSpecification(
ec, "Employee", fs, bindings);
// display just employees in department 1
for (int count = 0; count < specialEmployees.employees(); count++)
System.out.println((Employee)
(specialEmployees.objectAtIndex(count)));

249
Inserting and deleting objects
To add a row to a table in the database, create an instance of the corresponding class
generated by EOModeler. For instance, to create a new employee in the employee table,
create an instance of the Employee class, which EOModeler generated as
Employee.java. Set the appropriate values with the mutator methods, insert the object
into an EOEditingContext, and finally tell the EOEditingContext to save the changes.
Deleting a row is just as easy. To delete an object, tell the EOEditingContext which
contains the object to remove it, and then have the EOEditingContext save the changes.
The insertObject method is invoked on an instance of EOEditingContext to add an
object to the EOEditingContext.
instanceOfEOEditingContext.insertObject(EOEnterpriseObject someObject)

The deleteObject method is invoked on an instance of EOEditingContext to remove


the object.
instanceOfEOEditingContext.deleteObject(EOEnterpriseObject someObject)

Saving changes is done by invoking saveChanges on the instance of EOEditingContext


to which the objects have been added. At this point, EOF starts an actual transaction,
figures out which SQL statements need to be generated based on the changes you have
made within the editing context, issues those SQL statements, and issues a COMMIT.
instanceOfEOEditingContext.saveChanges()

Updating objects
By now, you should be able to guess how to update a row in a database.
1. Perform a fetch, qualifying on the primary key or some other attribute unique to
the row.
2. Check to make sure that only one object was fetched.
3. Use the mutator methods to set the appropriate attributes for the object within the
editing context.
4. Finally, save the changes.
Here's some sample code that updates the last name of employee 1 to be Pilgrim:
EOEditingContext ec = new EOEditingContext();
// get the fetch specification for qualifying with an name
EOFetchSpecification fs = EOFetchSpecificationNamed("employee",
"fetchByEmployeeId");
// set the bindings for the name

250
NSMutableDictionary bindings = new NSMutableDictionary();
bindings.takeValueForKey(new Integer("1"), "id");
// fetch the employees
NSArray employees =
(NSArray(EOUtilities.objectsWithFetchSpecification(
ec, "Employee", fs, bindings);
// make sure we found the employee we wanted
if (employees.count() == 1) {
// update the "last name" attribute within the editing context
Employee employee = (Employee)(employees.objectAtIndex(0));
employee.setLastName("Pilgrim");
}
// save the changes back to the database
ec.saveChanges();

Integrating Enterprise Objects Framework with JBoss


1. To use EOF with JBoss, the EOF frameworks need to be added to JBoss's class
path. The following folders should be copied from Apple/Library/Frameworks/
into the lib/ folder in your JBoss installation:
2.
o
o
o
o
o

JavaJDBCAdaptor.framework
JavaEOAccess.framework
JavaEOControl.framework
JavaFoundation.framework
JavaXML.framework

3. To add these to the class path, edit the main JBoss startup script (run.sh, not
run_with_tomcat.sh) and add these lines:

4. # add WebObjects frameworks


5. JBOSS_CLASSPATH=$JBOSS_CLASSPATH:../lib/JavaJDBCAdaptor.framework
6. JBOSS_CLASSPATH=$JBOSS_CLASSPATH:../lib/JavaJDBCAdaptor.framework/
Resources/Java/javajdbcadaptor.jar
7. JBOSS_CLASSPATH=$JBOSS_CLASSPATH:../lib/JavaEOAccess.framework
8. JBOSS_CLASSPATH=$JBOSS_CLASSPATH:../lib/JavaEOAccess.framework/Res
ources/Java/javaeoaccess.jar
9. JBOSS_CLASSPATH=$JBOSS_CLASSPATH:../lib/JavaEOControl.framework
10. JBOSS_CLASSPATH=$JBOSS_CLASSPATH:../lib/JavaEOControl.framework/
Resources/Java/javaeocontrol.jar
11. JBOSS_CLASSPATH=$JBOSS_CLASSPATH:../lib/JavaFoundation.framework
12. JBOSS_CLASSPATH=$JBOSS_CLASSPATH:../lib/JavaFoundation.framework
/Resources/Java/javafoundation.jar
13. JBOSS_CLASSPATH=$JBOSS_CLASSPATH:../lib/JavaXML.framework
14. JBOSS_CLASSPATH=$JBOSS_CLASSPATH:../lib/JavaXML.framework/Resour
ces/Java/javaxml.jar
15. (Under Windows, you would edit the run.bat startup script, like this:)
16. REM add WebObjects frameworks
17. set JBOSS_CLASSPATH=%JBOSS_CLASSPATH
%;../lib/JavaJDBCAdaptor.framework

251
18. set JBOSS_CLASSPATH=%JBOSS_CLASSPATH
%;../lib/JavaJDBCAdaptor.framework/Resources/Java/javajdbcadaptor.
jar
19. set JBOSS_CLASSPATH=%JBOSS_CLASSPATH
%;../lib/JavaEOAccess.framework
20. set JBOSS_CLASSPATH=%JBOSS_CLASSPATH
%;../lib/JavaEOAccess.framework/Resources/Java/javaeoaccess.jar
21. set JBOSS_CLASSPATH=%JBOSS_CLASSPATH
%;../lib/JavaEOControl.framework
22. set JBOSS_CLASSPATH=%JBOSS_CLASSPATH
%;../lib/JavaEOControl.framework/Resources/Java/javaeocontrol.jar
23. set JBOSS_CLASSPATH=%JBOSS_CLASSPATH
%;../lib/JavaFoundation.framework
24. set JBOSS_CLASSPATH=%JBOSS_CLASSPATH
%;../lib/JavaFoundation.framework/Resources/Java/javafoundation.ja
r
25. set JBOSS_CLASSPATH=%JBOSS_CLASSPATH%;../lib/JavaXML.framework
26. set JBOSS_CLASSPATH=%JBOSS_CLASSPATH
%;../lib/JavaXML.framework/Resources/Java/javaxml.jar

27. Your Enterprise Objects, which are the compiled classes from the .java files that
EOModeler generated, also need to be added in the same file. As we'll see later in
this chapter, these compiled classes will end up in a .jar file called
EmployeeEO.jar and placed deep within one of the framework directories. This
is where EOF expects to find it.

28. JBOSS_CLASSPATH=$JBOSS_CLASSPATH:../lib/JavaJDBCAdaptor.framewor
k/Resources/Java/EmployeeEO.jar

29.
That's it! Configuration of JBoss is complete. Now let's see what it takes to convert our
case study to use the Enterprise Objects Framework.

Converting the Case Study from BMP EJB to EOF


Since the case study was designed with a clear separation of presentation and business
logic, very few of the JSP files need any changes. The EJBs have been replaced by the
EOEnterpriseObjects classes (generated by EOModeler). That leaves only the Struts
Action classes, each of which will have to be changed to invoke the EOEnterpriseObjects
instead of the EJBs.
First, we will create a model from our existing employee database that we created in the
previous chapter. EOModeler can reverse-engineer an existing database to create a model
from it automatically.
(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:

252
1. Run EOModeler
2. From the menu, select Model, then New
3. The adaptor is always JDBC. (This is a historical legacy from the days before
WebObjects supported various native database drivers. These days, everything
goes through JDBC.)
4. Set up your database connection.
Username: admin
Password: admin
URL: jdbc:openbase://127.0.0.1/employee
(If you get an error saying that the database is not started, open OpenBase
Manager, select the employee database, and click Start.)
5. You should always include everything in your model (primary keys, relationships,
stored procedures, custom Enterprise Objects)
6. Include all 3 tables (CITY, DEPARTMENT, EMPLOYEE)
7. There shouldn't be any stored procedures, so just click Finish.
You should now have an untitled model. In order for WebObjects to find it at runtime, it
needs to be in a specific folder, so let's just save it there now. The correct location is deep
within your JBoss installation, in lib/JavaJDBCAdaptor.framework/Resources/. Call
it employee.eomodel.
Now let's look at the model we've created. It has 3 entities, City, Department, and
Employee. These should look familiar; they're the same as the tables in the underlying
database. Clicking on City reveals 3 attributes, cityId, name, and rowid. These
correspond to database columns. (You can ignore the rowid; that's just an internal
OpenBase pseudo-column.)
Similarly, the Department entity has attributes like departmentId, name, phone, and
secretaryId. Employee has cityId, departmentId, employeeId, extension,
firstname, and lastname.
Each entity should have one attribute with a key icon next to it; this is the primary key.
Note that some of the attribute names do not exactly match the underlying database
columns. For instance, the employee table has a primary key of employee_id, but it
shows up in EOModeler as employeeId. This is intentional; in your Java code, you will
refer to the employeeId attribute of the Employee class, and WebObjects will map that to
the original employee.employee_id database column.
Now let's create the fetch specifications we need. The first one we need is fetchById,
which will fetch an employee by the primary key, employeeId.
Do this:

253
1.
2.
3.
4.
5.

In EOModeler, select the Employee entity from the left-hand pane


From the menu, select Property, then Add Fetch Specification
Name the fetch specification fetchById
Select the employee attribute from the list of attributes
Type = $id to complete the expression. The complete expression should read
(employeeId = $id)

We also need additional fetch specifications for fetching all rows from each table.
Do this:
1.
2.
3.
4.

Select the Department entity from the left-hand pane


From the menu, select Property, then Add Fetch Specification
Name the fetch specification allDepartments
You don't have to set any qualifiers; the default is to select all rows, which is what
we want.
5. Select the City entity from the left-hand pane
6. From the menu, select Property, then Add Fetch Specification
7. Name the fetch specification allCities
8. Select the Employee entity from the left-hand pane
9. From the menu, select Property, then Add Fetch Specification
10. Name the fetch specification allEmployees
EOModeler can create .java files that are wrappers for accessing each entity in the model.
These are analagous to EJBs in a J2EE application, but they inherit from the WebObjects
framework classes instead of Sun's EJB framework classes. Under the covers,
EOModeler uses its own template language and a template file to generate these. This is
stored in

c:\Apple\Library\PrivateFrameworks\EOModelWizard.framework\Resources\EOJ
avaClass.template.

Unfortunately, this file needs a minor revision for use with Struts, which requires all
accessor and mutator methods to be named getXyz() and setXyz().
Do this:
1. Open the EOJavaClass.template file in a text editor.
2. Find ##loop $entity.classAttributes$.
3. Immediately after that is a line that looks like this:
public $property.javaValueClassName$ $property.name$() {

Change this to
public $property.javaValueClassName$ get$property.name$() {

OK, we're finally ready to generate the .java files from our model.

254
Do this:
1. In EOModeler, select the root node in the left-hand pane. If you saved your model
as employee.eomodel, then the node should be labeled employee. Do not just
select the Employee entity that corresponds to the employee database table. You
want the one above that, above Department, above City.
2. From the menu, select Property, then Generate Java files...
3. You will get prompted repeatedly to save City.java, Department.java, and
Employee.java. Save each of them in
d:\work\j2ee\code\Chapter8\Employee\EO\

And now we can create the rest of the files. As we mentioned earlier, most of the JSPs
and all of the Struts configuration files can be copied directly from the previous chapter's
code example. There's one JSP which needs to be changed, plus all the Action subclasses
for Struts. The EJBs are gone; they have been completely replaced by the .java files that
we just generated from EOModeler.
Do this:
1.
2.
3.
4.

Create a new web-application source hierarchy.


Copy all of the JSPs from the case study, except the employeeviewsuccess.jsp
Copy struts.jar into WEB-INF/lib/
Copy all the .xml, .tld, and the ApplicationResources.properties files into
WEB-INF/

5. Create a WEB-INF/classes/com/masslight/Employee/ hierarchy


6. Copy the following files listed below into the hierarchy
7. Double-check to make sure that your .eomodel (EOModeler model) is in
jboss/lib/JavaJDBCAdaptor.framework/Resources/

8. Run ant all


9. Test at http://localhost:8080/Employee/index.jsp
code/Chapter8/Employee
Employee
|
+-- employeeadd.jsp (*)
|
+-- employeeaddfailure.jsp (*)
|
+-- employeeaddsuccess.jsp (*)
|
+-- employeedeletefailure.jsp (*)
|
+-- employeedeletesuccess.jsp (*)
|
+-- employeeviewfailure.jsp (*)
|
+-- employeemodify.jsp (*)

255

code/Chapter8/Employee
|
+-|
+-|
+-|
+-|
+-|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|

index.jsp (*)
employeeviewsuccess.jsp (*)
build.xml (*)
employeemodifyfailure.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
|
|
|
+-- Chapter8Utils.java (*)
|
|
|
+-- com
|
|
|
+-- masslight
|
|
|
+-- Employee
|
|
|
+-- EmployeeAddForm.java (*)
|
|
|
+-- EmployeeAddAction.java (*)
|
|
|
+-- EmployeeModifyAction.java (*)
|
|
|
+-- EmployeeViewAction.java (*)
|
|
|
+-- EmployeeModifySetupAction.java (*)
|
|
|
+-- EmployeeModifyForm.java (*)
|
|

256

code/Chapter8/Employee
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+-|
|
|
+-- EO
|
+-|
+-|
+--

+-|
+-|
+-|
+--

EmployeeAddSetupAction.java (*)
EmployeeDeleteAction.java (*)
EmployeeModifySetupForm.java (*)
EmployeeDeleteForm.java (*)

lib
|
+-- struts.jar (*)

City.java (*)
Department.java (*)
Employee.java (*)

(*) denotes a file

code/Chapter8/Employee/employeeviewsuccess.jsp
<%@
<%@
<%@
<%@
<%@

page language="java" %>


taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
taglib uri="/WEB-INF/struts-form.tld" prefix="form" %>
taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>

<html:html locale="true">
<head>
<title>
My Co - Employee Directory
</title>
<html:base/>
</head>
<body bgcolor="white">
<h3>
My Co - Employee Directory
</h3>
<html:errors/>
<hr/>
<table border="1">
<tr>
<th>
Employee ID
</th>
<th>
First Name

257

code/Chapter8/Employee/employeeviewsuccess.jsp
</th>
<th>
Last Name
</th>
<th>
Extension
</th>
<th>
Department
</th>
<th>
City
</th>
<th>
</th>
<th>
</th>
</tr>
<logic:present name="employees">
<logic:iterate id="currentEmployee" name="employees">
<tr>
<td>
<bean:write name="currentEmployee" property="employeeId"/>
</td>
<td>
<bean:write name="currentEmployee" property="firstname"/>
</td>
<td>
<bean:write name="currentEmployee" property="lastname"/>
</td>
<td>
<bean:write name="currentEmployee" property="extension"/>
</td>
<td>
<bean:write name="currentEmployee" property="department.name"/>
</td>
<td>
<bean:write name="currentEmployee" property="city.name"/>
</td>
<td>
<a href="employeedelete.do?id=<bean:write
name="currentEmployee" property="employeeId"/>">Delete</a>
</td>
<td>
<a href="employeemodifysetup.do?id=<bean:write
name="currentEmployee" property="employeeId"/>">Modify</a>
</td>
</tr>
</logic:iterate>
</logic:present>
</table>
<hr/>
<html:link href="index.jsp">

258

code/Chapter8/Employee/employeeviewsuccess.jsp
Return to My Co - Main Menu
</html:link>
</body>
</html:html>

code/Chapter8/Employee/WEB-INF/classes/Chapter8Utils.java
import
import
import
import
import
import
import

Employee;
Department;
City;
java.util.*;
com.webobjects.eoaccess.*;
com.webobjects.eocontrol.*;
com.webobjects.foundation.*;

public class Chapter8Utils {


private static EOEditingContext ec = new EOEditingContext();
public static Vector fetchAllEmployees() {
NSMutableArray employees = null;
ec.lock();
employees = (NSMutableArray)
(EOUtilities.objectsWithFetchSpecificationAndBindings(
ec, "Employee", "allEmployees",
null));
ec.unlock();
Vector toReturn = new Vector();
for(int count = 0; count < employees.count(); count++) {
//
System.out.println(employees.objectAtIndex(count)); //
debugging
Employee employee = (Employee)(employees.objectAtIndex(count));
toReturn.addElement(employee);
}
return toReturn;
}
public static Employee fetchEmployeeById(Number id) throws Exception {
Employee employee;
NSMutableDictionary bindings = new NSMutableDictionary();
bindings.setObjectForKey(id, "id");

259

code/Chapter8/Employee/WEB-INF/classes/Chapter8Utils.java
ec.lock();
try {
employee =
(Employee)EOUtilities.objectWithFetchSpecificationAndBindings(
ec, "Employee", "fetchById", bindings);
System.out.println(employee); // debugging
} catch (Exception e) {
throw e;
}
ec.unlock();
return employee;
}
public static void addEmployee(Employee employee) throws Exception {
ec.lock();
try {
ec.insertObject(employee);
ec.saveChanges();
ec.invalidateAllObjects();
} catch (Exception e) {
throw e;
}
ec.unlock();
return;
}
public static void deleteEmployeeById(Number id) throws Exception {
Employee employee;
NSMutableDictionary bindings = new NSMutableDictionary();
bindings.setObjectForKey(id, "id");
ec.lock();
try {
employee =
(Employee)EOUtilities.objectWithFetchSpecificationAndBindings(
ec, "Employee", "fetchById", bindings);
ec.deleteObject(employee);
ec.saveChanges();
ec.invalidateAllObjects();
} catch (Exception e) {
throw e;
}
ec.unlock();
return;
}

260

code/Chapter8/Employee/WEB-INF/classes/Chapter8Utils.java
public static void modifyEmployee() throws Exception {
ec.lock();
try {
ec.saveChanges();
ec.invalidateAllObjects();
} catch (Exception e) {
throw e;
}
ec.unlock();
}
public static Vector fetchAllCities() {
NSArray cities;
ec.lock();
cities = EOUtilities.objectsWithFetchSpecificationAndBindings(
ec, "City", "allCities", null);
ec.unlock();
Vector toReturn = new Vector();
for(int count = 0; count < cities.count(); count++) {
City city = (City)cities.objectAtIndex(count);
toReturn.addElement(city);
}
return toReturn;
}
public static Vector fetchAllDepartments() {
NSArray departments;
ec.lock();
departments = EOUtilities.objectsWithFetchSpecificationAndBindings(
ec, "Department", "allDepartments", null);
ec.unlock();
Vector toReturn = new Vector();
for(int count = 0; count < departments.count(); count++) {
Department department =
(Department)departments.objectAtIndex(count);
toReturn.addElement(department);
}
return toReturn;

261

code/Chapter8/Employee/WEB-INF/classes/Chapter8Utils.java
}
}

code/Chapter8/Employee/WEBINF/classes/com/masslight/Employee/EmployeeAddForm.java
package com.masslight.Employee;
import javax.servlet.http.*;
import org.apache.struts.action.*;
public class EmployeeAddForm extends ActionForm {
private
private
private
private
private
private

Integer employeeid = new Integer(0);


String firstname = new String();
String lastname = new String();
String extension = new String();
Integer cityid = new Integer(0);
Integer departmentid = new Integer(0);

public EmployeeAddForm() {
}
public void reset(ActionMapping mapping, HttpServletRequest request) {
}
public ActionErrors validate(ActionMapping mapping, HttpServletRequest
request) {
ActionErrors errors = new ActionErrors();
}

return errors;

public
public
public
public
public
public

Integer getEmployeeid() { return employeeid; }


void setEmployeeid(Integer value) { employeeid = value; }
String getLastname() { return lastname; }
void setLastname(String value) { lastname = new String(value); }
String getFirstname() { return firstname; }
void setFirstname(String value) { firstname = new String(value);

public String getExtension() { return extension; }


public void setExtension(String value) { extension = new String(value);
}

public
public
public
public

Integer getDepartmentid() { return departmentid; }


void setDepartmentid(Integer value) { departmentid = value; }
Integer getCityid() { return cityid; }
void setCityid(Integer value) { cityid = value; }

262

code/Chapter8/Employee/WEBINF/classes/com/masslight/Employee/EmployeeAddSetupAction.java
package com.masslight.Employee;
import
import
import
import
import
import
import
import
import
import
import
import
import

Chapter8Utils;
java.util.*;
java.io.*;
java.lang.reflect.*;
java.util.*;
javax.servlet.*;
javax.servlet.http.*;
org.apache.struts.action.*;
org.apache.struts.util.*;
java.util.Collection;
com.webobjects.eocontrol.*;
com.webobjects.eoaccess.*;
com.webobjects.foundation.*;

public final class EmployeeAddSetupAction extends Action {


public EmployeeAddSetupAction() {
}
public ActionForward perform(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
Locale locale = getLocale(request);
MessageResources messages = getResources();
HttpSession session = request.getSession();
ActionErrors errors = new ActionErrors();
// insert logic here
try {
// set the cities
session.setAttribute("cities", Chapter8Utils.fetchAllCities());
// set the departments
session.setAttribute("departments",
Chapter8Utils.fetchAllDepartments());
} catch (Exception e) {
return (mapping.findForward("employeeaddsetupfailure"));
}
if (mapping.getAttribute() != null) {

263

code/Chapter8/Employee/WEBINF/classes/com/masslight/Employee/EmployeeAddSetupAction.java
if ("request".equals(mapping.getScope()))
request.removeAttribute(mapping.getAttribute());
else
session.removeAttribute(mapping.getAttribute());
}
return (mapping.findForward("employeeaddsetupsuccess"));
}
}

code/Chapter8/Employee/WEBINF/classes/com/masslight/Employee/EmployeeDeleteAction.java
package com.masslight.Employee;
import
import
import
import
import
import
import
import
import
import
import

Chapter8Utils;
java.util.*;
java.io.*;
java.lang.reflect.*;
javax.servlet.*;
javax.servlet.http.*;
org.apache.struts.action.*;
org.apache.struts.util.*;
com.webobjects.eocontrol.*;
com.webobjects.eoaccess.*;
com.webobjects.foundation.*;

public final class EmployeeDeleteAction extends Action {


public EmployeeDeleteAction() {
}
public ActionForward perform(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
Locale locale = getLocale(request);
MessageResources messages = getResources();
HttpSession session = request.getSession();
EmployeeDeleteForm employeeDeleteForm = (EmployeeDeleteForm)form;
ActionErrors errors = new ActionErrors();

264

code/Chapter8/Employee/WEBINF/classes/com/masslight/Employee/EmployeeDeleteAction.java
// insert logic here
try {
Chapter8Utils.deleteEmployeeById(employeeDeleteForm.getId());
} catch (Exception e) {
return (mapping.findForward("employeedeletefailure"));
}
if (mapping.getAttribute() != null) {
if ("request".equals(mapping.getScope()))
request.removeAttribute(mapping.getAttribute());
else
session.removeAttribute(mapping.getAttribute());
}
return (mapping.findForward("employeedeletesuccess"));
}
}

code/Chapter8/Employee/WEBINF/classes/com/masslight/Employee/EmployeeDeleteForm.java
package com.masslight.Employee;
import javax.servlet.http.*;
import org.apache.struts.action.*;
public class EmployeeDeleteForm extends ActionForm {
private Integer id;
public EmployeeDeleteForm() {
}
public Integer getId() { return id; }
public void setId(Integer value) { id = value; }
public void reset(ActionMapping mapping, HttpServletRequest request) {
}
public ActionErrors validate(ActionMapping mapping, HttpServletRequest
request) {
ActionErrors errors = new ActionErrors();
// validate fields here

265

code/Chapter8/Employee/WEBINF/classes/com/masslight/Employee/EmployeeDeleteForm.java
return errors;
}
}

code/Chapter8/Employee/WEBINF/classes/com/masslight/Employee/EmployeeModifyAction.java
package com.masslight.Employee;
import
import
import
import
import
import
import
import
import
import
import
import

Chapter8Utils;
Employee;
java.util.*;
java.io.*;
java.lang.reflect.*;
javax.servlet.*;
javax.servlet.http.*;
org.apache.struts.action.*;
org.apache.struts.util.*;
com.webobjects.eocontrol.*;
com.webobjects.eoaccess.*;
com.webobjects.foundation.*;

public final class EmployeeModifyAction extends Action {


public EmployeeModifyAction() {
}
public ActionForward perform(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
Locale locale = getLocale(request);
MessageResources messages = getResources();
HttpSession session = request.getSession();
EmployeeModifyForm employeeModifyForm = (EmployeeModifyForm)form;
ActionErrors errors = new ActionErrors();
// insert logic here
try {
System.out.println(employeeModifyForm.getEmployeeId());
System.out.println(employeeModifyForm.getFirstname());
System.out.println(employeeModifyForm.getLastname());
System.out.println(employeeModifyForm.getExtension());

266

code/Chapter8/Employee/WEBINF/classes/com/masslight/Employee/EmployeeModifyAction.java
System.out.println(employeeModifyForm.getCityId());
System.out.println(employeeModifyForm.getDepartmentId());
Employee employee =
Chapter8Utils.fetchEmployeeById(employeeModifyForm.getEmployeeId());
employee.setFirstname(employeeModifyForm.getFirstname());
employee.setLastname(employeeModifyForm.getLastname());
employee.setExtension(employeeModifyForm.getExtension());
employee.setCityId(employeeModifyForm.getCityId());
employee.setDepartmentId(employeeModifyForm.getDepartmentId());
Chapter8Utils.modifyEmployee();
} catch (Exception e) {
System.out.println(e); // debugging
e.printStackTrace(); // debugging
return (mapping.findForward("employeemodifyfailure"));
}
if (mapping.getAttribute() != null) {
if ("request".equals(mapping.getScope()))
request.removeAttribute(mapping.getAttribute());
else
session.removeAttribute(mapping.getAttribute());
}
return (mapping.findForward("employeemodifysuccess"));
}
}

code/Chapter8/Employee/WEBINF/classes/com/masslight/Employee/EmployeeModifyForm.java
package com.masslight.Employee;
import javax.servlet.http.*;
import org.apache.struts.action.*;
public class EmployeeModifyForm extends ActionForm {
private
private
private
private
private
private

Integer employeeid = new Integer(0);


String firstname = new String();
String lastname = new String();
String extension = new String();
Integer cityid = new Integer(0);
Integer departmentid = new Integer(0);

267

code/Chapter8/Employee/WEBINF/classes/com/masslight/Employee/EmployeeModifyForm.java
public EmployeeModifyForm() {
}
public void reset(ActionMapping mapping, HttpServletRequest request) {
}
public ActionErrors validate(ActionMapping mapping, HttpServletRequest
request) {
ActionErrors errors = new ActionErrors();
return errors;
}
public
public
public
public
public
public

Integer getEmployeeId() { return employeeid; }


void setEmployeeId(Integer value) { employeeid = value; }
String getLastname() { return lastname; }
void setLastname(String value) { lastname = new String(value); }
String getFirstname() { return firstname; }
void setFirstname(String value) { firstname = new String(value);

public String getExtension() { return extension; }


public void setExtension(String value) { extension = new String(value);

public
public
public
public

Integer getDepartmentId() { return departmentid; }


void setDepartmentId(Integer value) { departmentid = value; }
Integer getCityId() { return cityid; }
void setCityId(Integer value) { cityid = value; }

code/Chapter8/Employee/WEBINF/classes/com/masslight/Employee/EmployeeModifySetupAction.java
package com.masslight.Employee;
import
import
import
import
import
import
import
import

Chapter8Utils;
java.util.*;
java.io.*;
java.lang.reflect.*;
javax.servlet.*;
javax.servlet.http.*;
org.apache.struts.action.*;
org.apache.struts.util.*;

public final class EmployeeModifySetupAction extends Action {


public EmployeeModifySetupAction() {

268

code/Chapter8/Employee/WEBINF/classes/com/masslight/Employee/EmployeeModifySetupAction.java
}
public ActionForward perform(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
Locale locale = getLocale(request);
MessageResources messages = getResources();
HttpSession session = request.getSession();
EmployeeModifySetupForm employeeModifySetupForm =
(EmployeeModifySetupForm)form;
System.out.println(form);
ActionErrors errors = new ActionErrors();
// insert logic here
try {
// get all of the cities
session.setAttribute("cities", Chapter8Utils.fetchAllCities());
// get all of the departments
session.setAttribute("departments",
Chapter8Utils.fetchAllDepartments());
// get the employee by id
//
System.out.println(employeeModifySetupForm.getId()); //
debugging
session.setAttribute("employee",
Chapter8Utils.fetchEmployeeById(employeeModifySetupForm.getId()));
} catch (Exception e) {
return (mapping.findForward("employeemodifysetupfailure"));
}
if (mapping.getAttribute() != null) {
if ("request".equals(mapping.getScope()))
request.removeAttribute(mapping.getAttribute());
else
session.removeAttribute(mapping.getAttribute());
}
return (mapping.findForward("employeemodifysetupsuccess"));
}

269

code/Chapter8/Employee/WEBINF/classes/com/masslight/Employee/EmployeeModifySetupAction.java
}

code/Chapter8/Employee/WEBINF/classes/com/masslight/Employee/EmployeeModifySetupForm.java
package com.masslight.Employee;
import javax.servlet.http.*;
import org.apache.struts.action.*;
public class EmployeeModifySetupForm extends ActionForm {
private Integer id;
public EmployeeModifySetupForm() {
}
public Integer getId() { return id; }
public void setId(Integer value) { id = value; }
public void reset(ActionMapping mapping, HttpServletRequest request) {
}
public ActionErrors validate(ActionMapping mapping, HttpServletRequest
request) {
ActionErrors errors = new ActionErrors();
// validate fields here
}

return errors;

code/Chapter8/Employee/WEBINF/classes/com/masslight/Employee/EmployeeViewAction.java
package com.masslight.Employee;
import
import
import
import
import

Chapter8Utils;
java.util.*;
java.io.*;
java.lang.reflect.*;
java.util.*;

270

code/Chapter8/Employee/WEBINF/classes/com/masslight/Employee/EmployeeViewAction.java
import
import
import
import

javax.servlet.*;
javax.servlet.http.*;
org.apache.struts.action.*;
org.apache.struts.util.*;

public final class EmployeeViewAction extends Action {


public EmployeeViewAction() {
}
public ActionForward perform(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
Locale locale = getLocale(request);
MessageResources messages = getResources();
HttpSession session = request.getSession();
ActionErrors errors = new ActionErrors();
// insert logic here
try {
session.setAttribute("employees",
Chapter8Utils.fetchAllEmployees());
} catch (Exception e) {
return (mapping.findForward("employeeviewfailure"));
}
if (mapping.getAttribute() != null) {
if ("request".equals(mapping.getScope()))
request.removeAttribute(mapping.getAttribute());
else
session.removeAttribute(mapping.getAttribute());
}
return (mapping.findForward("employeeviewsuccess"));
}
}

code/Chapter8/Employee/build.xml
<project name="Employee" default="dist" basedir=".">

271

code/Chapter8/Employee/build.xml
<!-- set global properties for this build -->
<property environment="env"/>
<property name="top" value="."/>
<property name="src" value="."/>
<property name="build" value="build"/>
<property name="dist" value="dist"/>
<property name="war_dir" value="${dist}/lib"/>
<property name="war_file" value="${war_dir}/Employee.war"/>
<property name="eo_src" value="EO"/>
<property name="eo_build" value="eo_build"/>
<property name="eo_dist" value="eo_dist"/>
<property name="eo_jar_dir" value="${eo_dist}/lib"/>
<property name="eo_jar_file" value="${eo_jar_dir}/EmployeeEO.jar"/>
<property name="eo_deploy" value="$
{env.JBOSS_HOME}/lib/JavaJDBCAdaptor.framework/Resources/Java"/>
<property name="eo_classpath" value="$
{env.JBOSS_HOME}/lib/JavaJDBCAdaptor.framework;
$
{env.JBOSS_HOME}/lib/JavaJDBCAdaptor.framework/Resources/Java/javajdbcadaptor.jar;
${env.JBOSS_HOME}/lib/JavaEOAccess.framework;
${env.JBOSS_HOME}/lib/JavaEOAccess.framework/Resources/Java/javaeoaccess.jar;
${env.JBOSS_HOME}/lib/JavaEOControl.framework;
${env.JBOSS_HOME}/lib/JavaEOControl.framework/Resources/Java/javaeocontrol.jar;
${env.JBOSS_HOME}/lib/JavaFoundation.framework;
$
{env.JBOSS_HOME}/lib/JavaFoundation.framework/Resources/Java/javafoundation.jar;
${env.JBOSS_HOME}/lib/JavaXML.framework;
${env.JBOSS_HOME}/lib/JavaXML.framework/Resources/Java/javaxml.jar"/>
<property name="webinf" value="${top}/WEB-INF"/>
<property name="web.xml" value="${webinf}/web.xml"/>
<property name="classes" value="${webinf}/classes"/>
<property name="lib" value="${top}/WEB-INF/lib"/>
<property name="struts.jar" value="${env.TOMCAT_HOME}/lib/servlet.jar;$
{webinf}/lib/struts.jar"/>
<property name="deploy" value="${env.JBOSS_HOME}/deploy"/>
<target name="clean">
<!-- Delete our the ${build} and ${dist} directory trees -->
<delete dir="${build}"/>
<delete dir="${eo_build}"/>
<delete dir="${dist}"/>
<delete dir="${eo_dist}"/>
<delete dir="${war_dir}"/>
<delete dir="${eo_jar_dir}"/>
</target>
<target name="init">
<!-- Create the build directory structure used by compile and dist -->
<mkdir dir="${build}"/>
<mkdir dir="${eo_build}"/>

272

code/Chapter8/Employee/build.xml
<mkdir dir="${dist}"/>
<mkdir dir="${eo_dist}"/>
<mkdir dir="${war_dir}"/>
<mkdir dir="${eo_jar_dir}"/>
</target>
<target name="compileeo" depends="init">
<!-- Compile the EOF objects generated by EOModeler -->
<javac
srcdir="${top}/${eo_src}"
destdir="${eo_build}"
classpath="${eo_classpath}"/>
</target>
<target name="compilestruts" depends="init,compileeo">
<!-- Compile the java code from ${src} into ${build} -->
<javac
srcdir="${top}/${src}"
destdir="${build}"
classpath="${struts.jar};${eo_classpath};${eo_jar_file}"/>
</target>
<target name="compile" depends="compileeo,compilestruts"/>
<target name="diststruts" depends="compilestruts">
<!-- Put everything in a war file -->
<war warfile="${war_file}" webxml="${web.xml}">
<!-- include all JSPs in root level, and all .properties files anywhere -->
<fileset dir="${top}/${src}">
<include name="*.jsp"/>
<include name="**/*.properties"/>
</fileset>
<!-- include all tag libraries in WEB-INF, and all .xml config files,
but not web.xml (that's handled separately) -->
<webinf dir="${webinf}">
<include name="*.tld"/>
<include name="*.xml"/>
<exclude name="web.xml"/>
</webinf>
<!-- include all libraries in WEB-INF/lib (like struts.jar) -->
<lib dir="${lib}"/>
<!-- include all compiled classes -->
<classes dir="${build}"/>
</war>
</target>
<target name="disteo" depends="compileeo">
<jar jarfile="${eo_jar_file}">
<!-- include all compiled class files -->
<fileset dir="${eo_build}">

273

code/Chapter8/Employee/build.xml
<include name="*.class"/>
</fileset>
</jar>
</target>
<target name="dist" depends="disteo,diststruts"/>
<target name="deploystruts">
<!-- Copy the war file to the JBoss deploy directory -->
<copy file="${war_file}" todir="${deploy}"/>
</target>
<target name="deployeo">
<!-- Copy the jar file to the directory -->
<copy file="${eo_jar_file}" todir="${eo_deploy}"/>
</target>
<target name="deploy" depends="deployeo,deploystruts"/>
<target name="all" depends="clean,dist,deploy"/>
</project>

Exercises

Exercise 1. Think

Discuss the benefits of using an O/R mapping framework. Compare the development
times of the EJB and EOF versions of the applications. Also, compare the complexity of
each application. What implications are caused by database schema changes.

You might also like