Professional Documents
Culture Documents
Table of Contents
1.
2.
3.
4.
5.
6.
7.
8.
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
Goals
Prerequisites
Objectives
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:
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.
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>
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" ... %>
<%@ page ... %> specifies information that affects the page
<%@ include ... %> includes a file at the location of the include
directive
(parsed)
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.)
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.
Code/Chapter1/MyGreeting/HelloWorld.jsp
<%
Hello World!<BR>
}
%>
</BODY>
</HTML>
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
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
make a JavaBean.
Objectives
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.
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.
response
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
Out
Config
Page
Exception
13
method name
description
HttpSession getSession()
String
getHeader(String headerName)
Enumeration getHeaderNames()
Cookie[]
getCookies()
Object
getAttribute(String
attributeName)
setAttribute(String
nameOfTheAttribute, Object
valueOfTheAttribute)
void
nameOfTheAttribute to
valueOfTheAttribute
the value of
method name
description
void
addCookie(Cookie cookie)
void
void
void
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>
15
return type
method name
description
Object
findAttribute(String
attributeName)
Object
getAttribute(String
attributeName)
void
setAttribute(String
nameOfTheAttribute,
Object
valueOfTheAttribute)
HttpServletRequest
getRequest()
HttpServletResponse getResponse()
nameOfTheAttribute to
valueOfTheAttribute
the value of
method name
description
Object
getAttribute(String
attributeName)
setAttribute(String
nameOfTheAttribute, Object
valueOfTheAttribute)
void
String[] getAttributeNames()
nameOfTheAttribute to
valueOfTheAttribute
the value of
method name
description
Object
getAttribute(String
attributeName)
setAttribute(String
nameOfTheAttribute, Object
valueOfTheAttribute)
void
Enumeration getAttributeNames()
nameOfTheAttribute to
valueOfTheAttribute
the value of
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>
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() %>
18
code/Chapter2/FirstBean
FirstBean
|
+-- beanExample.jsp (*)
|
+-- WEB-INF
|
+-- web.xml (*)
|
+-- classes
|
+-- com
|
+-- masslight
|
+-- beans
|
+-- GreeterBean.java (*)
(*) denotes a file
19
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\
20
type variable_type and name exampleVariable would be:
If the variable_type is boolean, the set method would be the same and the get method
would be:
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
21
code/Chapter2/SecondBean
|
+-- NumberBean.java (*)
(*) denotes a file
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
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.
23
code/Chapter2/SerializableBean
+-- firstPage.jsp (*)
|
+-- secondPage.jsp (*)
|
+-- WEB-INF
|
+-- web.xml (*)
|
+-- classes
|
+-- com
|
+-- masslight
|
+-- beans
|
+-- SerializableBean.java (*)
(*) denotes a file
<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>
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.
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
Goals
Objectives
The purpose of this chapter is to provide the student with the necessary
information about tags and tag libraries.
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/>
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
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
tlibversion
jspversion
shortname
uri
info
tag
tagclass
teiclass
bodycontent
info
for comments
attribute
the name of the attribute that the tag might need in the declaration
- required
required
rtexprvalue
30
31
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
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:
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
35
<target name="all" depends="clean,dist,deploy"/>
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>
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"/>
38
code/Chapter3/AttributeTag/tagExample.jsp
<html>
<body>
<greeter:Greeting greeting="Hey" />
</body>
</html>
39
code/Chapter3/AttributeTag/WEBINF/classes/com/masslight/tagExampleClasses/GreetingAttributeTag.java
}
public void setGreeting(String newGreeting) {
greeting = newGreeting;
}
}
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"/>
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:
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
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>
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
Goals
Prerequisites
Objectives
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:
name
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
nested
indexed
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);
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>
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.
<bean:message
... >
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"/>
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>
<html:cancel>
<html:checkbox>
<html:multibox>
Wood
Stone
Sheep
Clay
54
<html:checkbox>
<html:errors>
<html:file>
<html:form>
Generates <form>.
<html:hidden>
<html:html>
Generates <html>.
<html:image>
<html:img>
<html:link>
<html:password>
<html:radio>
Debit
<html:reset>
55
<html:select>
<html:options>
<html:option>
<html:select>
Generates <select>.
<html:options>
tags.
<html:option>
Submit
<html:submit>
Email Address
Generates <input type="text">.
<html:textarea>
Generates <textarea>.
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
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
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>
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>
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>
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;
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.*;
67
code/Chapter4/SimpleStruts/WEBINF/classes/com/masslight/strutsExampleClasses/SetNameAction.java
servlet.log("SetNameAction:
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
<%@
<%@
<%@
<%@
<%@
<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>
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:
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
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
Objectives
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.
73
logic:present most commonly checks for the presence
using the cookie, header, or name attributes. Remember
<logic:present name="person">
<bean:write name="person" property="firstName"/>
...
...
</logic:present>
property
cookie
header
name
parameter
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
user
Checks to see if the currently authenticated user has the specified name
cookie
header
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
74
property
scope
the scope within which to search for the bean specified by name
web.xml
changed.
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
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>
%>" >
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>
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
length
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
property
type
scope
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 (*)
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>
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.*;
83
code/Chapter5/IteratorStruts/WEBINF/classes/com/masslight/actions/SetListAction.java
}
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
return type
method name
description
long
getDateHeader(String
nameOfHeader)
String
getHeader(String
nameOfHeader)
Enumeration getHeaderNames()
nameOfHeader
Enumeration
getHeaders(String
nameOfHeader)
int
HTTPServletResponse
return
type
method name
description
void
addDateHeader(String
nameOfHeader, long
date)
void
addHeader(String
nameOfHeader, String
value)
85
void
addIntHeader(String
nameOfHeader, int
value)
boolean
containsHeader(String
nameOfHeader)
void
setDateHeader(String
nameOfHeader, long
date)
void
setHeader(String
nameOfHeader, String
value)
void
setIntHeader(String
nameOfHeader, int
value)
tag properties
property name
description
id
messages
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
86
property name
value
description
the value to return if no header exists with the name specified in the
name property
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
messages
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
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
value
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
89
String newValue = "" + System.currentTimeMillis();
info = new Cookie("MyCookie", newValue);
info.setMaxAge(60);
info.setPath("/");
response.addCookie(info);
90
code/Chapter5/CookieStruts
+-- strutsExampleClasses
|
+-- ApplicationResources.properties (*)
|
+-- NameForm.java (*)
|
+-- Name.java (*)
|
+-- SetNameAction.java (*)
(*) denotes a file
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
Goals
Prerequisites
Objectives
93
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
94
EJB containers.
Directory
service
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.
96
element
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.
98
99
com.masslight.ExampleEJB.Home home = (com.masslight.ExampleEJB.Home)
PortableRemoteObject.narrow (reference,
com.masslight.ExampleEJB.Home.class);
Invoke a business method from an instance of the bean class - in a try/catch statement
remote.businessMethodOne(...);
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>
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
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.
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.
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
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>
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.*;
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
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.
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 (*)
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"));
}
}
javax.naming.*;
java.util.Hashtable;
javax.rmi.PortableRemoteObject;
javax.ejb.*;
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"/>
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
// no corresponding property
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
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
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
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
javax.ejb.EJBObject
method declaration
description
132
public abstract boolean isIdentical(EJBObject
obj) throws java.rmi.RemoteException;
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
javax.jts.UserTransaction
getUserTransaction() throws
java.lang.IllegalStateException;
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
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
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
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
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>
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
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();
}
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();
}
try {
connection.close();
} catch (SQLException e) {
System.out.println("An SQL Exception occurred while closing the
connection");
}
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
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.*;
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");
}
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;
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
/////////////////////////////////////////////////////////////////////////////
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");
}
{
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;
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);
}
}
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"/>
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>
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.
160
Goals
Objectives
The objective for this chapter is to combine all of the technologies learned
thus far in the course into one application, demonstrating how each
technology works with the other technologies to create powerful and 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.
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>
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();
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
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();
}
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();
}
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' ,
'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)
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
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.");
}
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
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
////////////////////////////////////////////////////////////////////////////
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");
}
}
////////////////////////////////////////////////////////////////////////////
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");
}
////////////////////////////////////////////////////////////////////////////
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
////////////////////////////////////////////////////////////////////////////
/
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();
}
////////////////////////////////////////////////////////////////////////////
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
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());
}
}
////////////////////////////////////////////////////////////////////////////
try {
insertRow(cityid, name);
} catch (Exception ex) {
throw new EJBException("ejbCreate: " + ex.getMessage());
}
setCityid(cityid);
setName(name);
return cityid;
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");
}
}
////////////////////////////////////////////////////////////////////////////
190
code/Chapter7/Employee/EmployeeEJB/com/masslight/City/CityEJB.java
try {
loadRow();
} catch (Exception ex) {
throw new EJBException("ejbLoad: " + ex.getMessage());
}
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();
}
////////////////////////////////////////////////////////////////////////////
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
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();
////////////////////////////////////////////////////////////////////////////
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");
}
}
////////////////////////////////////////////////////////////////////////////
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
////////////////////////////////////////////////////////////////////////////
199
code/Chapter7/Employee/EmployeeEJB/com/masslight/Department/DepartmentEJB.java
}
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;
}
////////////////////////////////////////////////////////////////////////////
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);
}
}
202
JSPs
The JSPs for the application are shown below.
code/Chapter7/Employee/EmployeeClient/index.jsp
<%@
<%@
<%@
<%@
<%@
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
<%@
<%@
<%@
<%@
<%@
<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
<%@
<%@
<%@
<%@
<%@
<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
<%@
<%@
<%@
<%@
<%@
<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
<%@
<%@
<%@
<%@
<%@
<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
<%@
<%@
<%@
<%@
<%@
<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
<%@
<%@
<%@
<%@
<%@
<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
<%@
<%@
<%@
<%@
<%@
<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
<%@
<%@
<%@
<%@
<%@
<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
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
public
public
public
public
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.*;
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.*;
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
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
public
public
public
public
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.*;
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
}
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>
229
http://.../employeeaddsetup.do is requested. The container knows from strutsconfig.xml that an instance of com.masslight.Employee.EmployeeAddSetupAction
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>
231
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
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.
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
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
Prerequisites
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.
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)
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();
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:
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.
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.
We also need additional fetch specifications for fetching all rows from each table.
Do this:
1.
2.
3.
4.
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.
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 (*)
code/Chapter8/Employee/employeeviewsuccess.jsp
<%@
<%@
<%@
<%@
<%@
<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.*;
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
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
public
public
public
public
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.*;
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.*;
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.*;
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
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
public
public
public
public
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.*;
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.*;
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.