We're just full of beans on the Developer Relations Technical Consulting team. JavaBeans components, that is. We use them in our three-tier Go-ForIt.com application, as follows:
- First tier
- The browser.
- Second tier
- Servlets, JavaServer Pages (JSPs) technology, and a bunch of specialized JavaBeans components.
- Third tier
- Business logic, in Enterprise JavaBeans (EJB) technology, and a database.
The application is designed so each tier can reside on a separate machine. We think this is cool because when our user base grows, we can effectively scale using WebSphere's WorkLoad Management functions.
We decided to use client-side beans in response to a design challenge. We define requirements in terms of user stories. Our first user story, "The user registers with the Go-ForIt.com Web site," forced us to make a critical design decision. The figure below shows one solution to the problem.
Architecture before client-side bean

Our application has two types of users: customers and personal assistants (PAs). Because the user information has to be stored in a database, it is a persistent entity, and we implement it as a container-managed persistent EJB component on the third tier of the application.
In our application, the first thing users have to do is register. They complete all the required profile information in the form and send it to the registration servlet (on the middle tier). The servlet sends this data over to the User EJB component, so it can be persisted. The servlet will have to do a look-up on the User EJB component, get the component's home interface and call its create method, then get the component's remote interface and call individual set methods for each field. The current version of the User class has 20 fields, which means that the servlet has to call 20 set methods. That's a lot of remote method calls just to create a user.
When we started implementing the second user story, "User changes the user profile," guess what? We had to first go and get all 20 fields from the database with the EJB component, using servlets, and display them using JSPs technology. Then, when the user changed the profile, we had to go back and persist the change. This meant a lot of remote method calls between our middle and third tier. And more remote calls meant decreased performance. We needed a more elegant solution.
Reusable components are simply pre-built pieces of programming code designed to do a specific function. They help reduce the development time for applications. JavaBeans components are "capsules" of code, each designed for a specific purpose. The advantage of the JavaBeans component architecture over standard programming components is that JavaBeans components are independent. A JavaBeans component and a server bean, more commonly known as an Enterprise JavaBeans (EJB) component, have some similarities. They are both objects or components created with a set of characteristics to do a specific job. An EJB component is used on the server side to represent back-end business logic. An invisible JavaBeans component can be used either as a shared resource within a GUI application, or as a component in building server applications. JavaBeans components can be paired with EJB components to do business functions such as errands, credit card validation, and so on.
What do we mean by client-side beans?
The servlets are clients to EJB components. In our example, the registration servlet is a client to the User EJB component. The servlets can effectively use a JavaBeans component that captures the user profile, and the profile can be passed to the User EJB component. Since these JavaBeans components reside on the middle tier, which is a client to the EJB component, we call them client-side beans.
Another important use of the client-side beans is that they can be consumed by the JSPs, or other servlets, or client-side components and be reused. This reduces the trips to the server to get the data. In our example, user_profile_edit.jsp will use the User Data bean to get values of the different fields to display when users need to edit their profile.
The next dilemma we faced was to determine which components would be Java classes and which would be JavaBeans components. All of our objects can easily be represented as a Java class. We had to have a scheme to decide which Java classes should be beans, so we asked the following questions:
- Can this piece of code be used in more than one area? Would others benefit from reusing this piece of code?
- Can you quickly think of ways that this piece of code might be customized?
- Is the purpose of this code easy to explain?
- Does the code module contain all the information it needs to work by itself? Does it have good encapsulation?
- Do these classes store all the data information and have none or little behavior information in them?
If we answered yes to all of the questions, we made the class a bean. You can make any Java class into a bean by changing the class to adhere to the JavaBeans APIs specification. For the client-side beans we used the Serializable interface. Because we're using eXtreme Programming (XP) we let the individual component developer use the questions above to decide whether to design a component as a bean or a Java class. (See "eXtreme programming: deceptively simple innovation" for information on XP.)
The figure below shows our application architecture after using client-side beans.
Architecture after client-side beans

How do client-side beans work?
When users register on the site, we collect information: their first and last name, address, phone number, whether they're a customer or PA or both, credit card information, user ID, and password. That information is then stored in the database.
Because the users need to be persistent and participate in a transaction, they are represented as an Entity bean that resides in a server-side EJB container. The HTML page invokes the registration servlet. The registration servlet is on the client-side Web container. It reads all the parameters from the request object and sets it in the
user object, using the set methods. Setting each parameter individually requires invoking the corresponding set method. Since we didn't want to make remote method calls on the User EJB component, we used a client-side bean called the UserDataBean. This bean is in the same Web container as the servlets on the client side. The servlet instantiates a UserDataBean and then sets all the parameters in it, as shown in the code example below.
Listing 1: Registration Servlet sets the data in UserDataBean
\** This is sample code for performTask method of Registration Servlet**\
public void performTask(
javax.servlet.http.HttpServletRequest req,
javax.servlet.http.HttpServletResponse res) {
//instantiate the Client Side Bean
UserDataBean user = new UserDataBean();
UserRegistersCommandBean dataCommand = new UserRegistersCommandBean();
try {
/*
* Retrieve the form fields
*/
user.setAltphone(req.getParameter("alternatePhone"));
user.setCcname(req.getParameter("nameOnCreditCard"));
user.setCcnum(req.getParameter("creditCardNumber"));
user.setCctype(req.getParameter("creditCardType"));
// convert from the string to an SQL date type.
// Day doesn't matter so we use 28 in all cases
String dateString =
req.getParameter("expirationYear")
+ "-"
+ req.getParameter("expirationMonth")
+ "-28";
java.sql.Date date = java.sql.Date.valueOf(dateString);
user.setExpdate(date);
//
user.setFname(req.getParameter("firstName"));
user.setMidinit(req.getParameter("middleInitial"));
user.setLname(req.getParameter("lastName"));
user.setPassword(req.getParameter("password"));
user.setCity(req.getParameter("city"));
user.setStreet1(req.getParameter("address1"));
user.setStreet2(req.getParameter("address2"));
user.setTitle(req.getParameter("title"));
user.setTstamp(Long.toString(System.currentTimeMillis()));
user.setCctype(req.getParameter("creditCardType"));
user.setUserid(req.getParameter("userid"));
user.setState(req.getParameter("state"));
user.setZip(req.getParameter("zipCode"));
user.setEmail(req.getParameter("email"));
user.setPhone(req.getParameter("phone"));
user.setType(getUserType(req));
/*
* Update database with new user information
*/
try {
dataCommand.setUser(user);
dataCommand.execute();
} catch (Exception e) {
.........
}
/* Create/update session object to record new user information */
HttpSession session = null;
session = req.getSession(true);
session.putValue("user", user);
req.setAttribute("user", user);
String userType = user.getType();
/* Forward to HTML View JSP */
if (userType.equals(UserDataBean.CUSTOMER)) {
/* Forward to HTML View JSP */
getServletConfig()
.getServletContext()
.getRequestDispatcher("pages/customer_main_menu.jsp")
.forward(req, res);
} else {
/* set the paErrandList in the session to null */
com.goforit.ejb.Errand paErrandList = null;
session.putValue("paErrandList", paErrandList);
if (userType.equals(UserDataBean.PA)) {
/* Fill in PA Stuff */
getServletConfig()
.getServletContext()
.getRequestDispatcher("pages/pa_profile_edit.jsp")
.forward(req, res);
} else {
/* Forward to HTML View JSP */
getServletConfig()
.getServletContext()
.getRequestDispatcher("pages/pa_profile_edit.jsp")
.forward(req, res);
}
}
} catch (Throwable t) {
........
}
|
\** This is sample code for performTask method of Registration Servlet**\
public void performTask(
javax.servlet.http.HttpServletRequest req,
javax.servlet.http.HttpServletResponse res) {
//instantiate the Client Side Bean
UserDataBean user = new UserDataBean();
UserRegistersCommandBean dataCommand = new UserRegistersCommandBean();
try {
/*
* Retrieve the form fields
*/
user.setAltphone(req.getParameter("alternatePhone"));
user.setCcname(req.getParameter("nameOnCreditCard"));
user.setCcnum(req.getParameter("creditCardNumber"));
user.setCctype(req.getParameter("creditCardType"));
// convert from the string to an SQL date type.
// Day doesn't matter so we use 28 in all cases
String dateString =
req.getParameter("expirationYear")
+ "-"
+ req.getParameter("expirationMonth")
+ "-28";
java.sql.Date date = java.sql.Date.valueOf(dateString);
user.setExpdate(date);
//
user.setFname(req.getParameter("firstName"));
user.setMidinit(req.getParameter("middleInitial"));
user.setLname(req.getParameter("lastName"));
user.setPassword(req.getParameter("password"));
user.setCity(req.getParameter("city"));
user.setStreet1(req.getParameter("address1"));
user.setStreet2(req.getParameter("address2"));
user.setTitle(req.getParameter("title"));
user.setTstamp(Long.toString(System.currentTimeMillis()));
user.setCctype(req.getParameter("creditCardType"));
user.setUserid(req.getParameter("userid"));
user.setState(req.getParameter("state"));
user.setZip(req.getParameter("zipCode"));
user.setEmail(req.getParameter("email"));
user.setPhone(req.getParameter("phone"));
user.setType(getUserType(req));
/*
* Update database with new user information
*/
try {
dataCommand.setUser(user);
dataCommand.execute();
} catch (Exception e) {
.........
}
/* Create/update session object to record new user information */
HttpSession session = null;
session = req.getSession(true);
session.putValue("user", user);
req.setAttribute("user", user);
String userType = user.getType();
/* Forward to HTML View JSP */
if (userType.equals(UserDataBean.CUSTOMER)) {
/* Forward to HTML View JSP */
getServletConfig()
.getServletContext()
.getRequestDispatcher("pages/customer_main_menu.jsp")
.forward(req, res);
} else {
/* set the paErrandList in the session to null */
com.goforit.ejb.Errand paErrandList = null;
session.putValue("paErrandList", paErrandList);
if (userType.equals(UserDataBean.PA)) {
/* Fill in PA Stuff */
getServletConfig()
.getServletContext()
.getRequestDispatcher("pages/pa_profile_edit.jsp")
.forward(req, res);
} else {
/* Forward to HTML View JSP */
getServletConfig()
.getServletContext()
.getRequestDispatcher("pages/pa_profile_edit.jsp")
.forward(req, res);
}
}
} catch (Throwable t) {
........
}
|
Now we have an object that represents all the user data, we can pass this object on the server side to the User EJB component with the help of a command bean. The persistent file IDs of the User EJB component are initialized in its create method, as shown below.
Listing 2: Populating the User EJB with user data on the server side
/**
* ejbCreate method for a CMP entity bean
* @param argUserid java.lang.String
* @exception javax.ejb.CreateException The exception description.
* @exception java.rmi.RemoteException The exception description.
*/
public void ejbCreate(com.goforit.user.UserDataBean argUser)
throws javax.ejb.CreateException, java.rmi.RemoteException {
// All CMP fields should be initialized here.
userid = argUser.getUserid();
altphone = argUser.getAltphone();
ccexpdate = argUser.getExpdate();
ccname = argUser.getCcname();
ccnumber = argUser.getCcnum();
cctype = argUser.getCctype();
city = argUser.getCity();
email = argUser.getEmail();
firstname = argUser.getFname();
lastname = argUser.getLname();
midinitial = argUser.getMidinit();
password = argUser.getPassword();
phone = argUser.getPhone();
state = argUser.getState();
street1 = argUser.getStreet1();
street2 = argUser.getStreet2();
timestamp = argUser.getTstamp();
title = argUser.getTitle();
type = argUser.getType();
zip = argUser.getZip(); |
/**
* ejbCreate method for a CMP entity bean
* @param argUserid java.lang.String
* @exception javax.ejb.CreateException The exception description.
* @exception java.rmi.RemoteException The exception description.
*/
public void ejbCreate(com.goforit.user.UserDataBean argUser)
throws javax.ejb.CreateException, java.rmi.RemoteException {
// All CMP fields should be initialized here.
userid = argUser.getUserid();
altphone = argUser.getAltphone();
ccexpdate = argUser.getExpdate();
ccname = argUser.getCcname();
ccnumber = argUser.getCcnum();
cctype = argUser.getCctype();
city = argUser.getCity();
email = argUser.getEmail();
firstname = argUser.getFname();
lastname = argUser.getLname();
midinitial = argUser.getMidinit();
password = argUser.getPassword();
phone = argUser.getPhone();
state = argUser.getState();
street1 = argUser.getStreet1();
street2 = argUser.getStreet2();
timestamp = argUser.getTstamp();
title = argUser.getTitle();
type = argUser.getType();
zip = argUser.getZip(); |
I've shown how to use UserDataBean to initialize
the User EJB component. Now, let's say I need to activate the object from the database and display its values in a JSP servlet, so users can update their profile information. Think of the amount of network traffic for such a data-intensive object! I will again seek help from UserDataBean.
Our team has a UserController EJB that does the controller function for User EJB objects on the server side. It has a method getUserInfo(String userid). Using userid, we find the corresponding User EJB. Then we instantiate the UserDataBean and set its field value from the corresponding field values of the User EJB. Finally we return the UserDataBean back to the client side, as shown below.
Listing 3: UserController EJB retrieving user data from the User EJB and setting in UserDataBean
public UserDataBean getUserInfo(String userid)
throws java.rmi.RemoteException {
UserDataBean userDataBean = null;
try {
// Get User info
User user =
_userHome.findByPrimaryKey(new UserKey(userid));
//instantiate the Client Side Bean
userDataBean = new UserDataBean();
userDataBean.setUserid(userid);
userDataBean.setAltphone(user.getAltphone());
userDataBean.setCcname(user.getCcname());
userDataBean.setCcnum(user.getCcnumber());
userDataBean.setCctype(user.getCctype());
userDataBean.setCity(user.getCity());
userDataBean.setEmail(user.getEmail());
userDataBean.setExpdate(user.getCcexpdate());
userDataBean.setFname(user.getFirstname());
userDataBean.setLname(user.getLastname());
userDataBean.setMidinit(user.getMidinitial());
userDataBean.setPassword(user.getPassword());
userDataBean.setPhone(user.getPhone());
userDataBean.setState(user.getState());
userDataBean.setStreet1(user.getStreet1());
userDataBean.setStreet2(user.getStreet2());
userDataBean.setTitle(user.getTitle());
userDataBean.setTstamp(user.getTimestamp());
userDataBean.setType(user.getType());
userDataBean.setZip(user.getZip());
} catch (ObjectNotFoundException e) {
return userDataBean;
} catch (Exception e) {
throw new java.rmi.RemoteException(e.toString());
}
return userDataBean;
|
public UserDataBean getUserInfo(String userid)
throws java.rmi.RemoteException {
UserDataBean userDataBean = null;
try {
// Get User info
User user =
_userHome.findByPrimaryKey(new UserKey(userid));
//instantiate the Client Side Bean
userDataBean = new UserDataBean();
userDataBean.setUserid(userid);
userDataBean.setAltphone(user.getAltphone());
userDataBean.setCcname(user.getCcname());
userDataBean.setCcnum(user.getCcnumber());
userDataBean.setCctype(user.getCctype());
userDataBean.setCity(user.getCity());
userDataBean.setEmail(user.getEmail());
userDataBean.setExpdate(user.getCcexpdate());
userDataBean.setFname(user.getFirstname());
userDataBean.setLname(user.getLastname());
userDataBean.setMidinit(user.getMidinitial());
userDataBean.setPassword(user.getPassword());
userDataBean.setPhone(user.getPhone());
userDataBean.setState(user.getState());
userDataBean.setStreet1(user.getStreet1());
userDataBean.setStreet2(user.getStreet2());
userDataBean.setTitle(user.getTitle());
userDataBean.setTstamp(user.getTimestamp());
userDataBean.setType(user.getType());
userDataBean.setZip(user.getZip());
} catch (ObjectNotFoundException e) {
return userDataBean;
} catch (Exception e) {
throw new java.rmi.RemoteException(e.toString());
}
return userDataBean;
|
The EditUserProfileServlet sets the above UserDataBean in the request object and invokes user_profile_edit.jsp. So user_profile_edit.jsp now
has access to the above bean using the <jsp:useBean> tag. We then retrieve individual properties using the <%jsp:getProperty> tag or a JSP expression. The following code sample shows the JSP expression.
Listing 4: user_profile_edit.jsp displays the user data for user to edit
<TD class="head">Edit User Information</TD>
........
<jsp:useBean id="user" class="com.goforit.user.UserDataBean" scope="session">
</jsp:useBean>
........
<TD class="subhead">Please update the following information as required:</TD>
........
<FORM action="/goforit/UpdateUserProfileServlet"
method="POST" onsubmit="return checkForm(this);">
........
<TD width="375" align="right">First Name:</TD>
<TD>
<%--METADATA type="DynamicData" startspan
<INPUT size="20" type="text" maxlength="25" name="firstName"
valueproperty="user.fname" dynamicelement>--%>
<INPUT maxlength="25" name="firstName" size="20"
type="text" value="<%= user.getFname() %>">
<%--METADATA type="DynamicData" endspan--%>
</TD>
</TR>
<TR>
<TD width="375" align="right">Last Name:</TD>
<TD>
<%--METADATA type="DynamicData" startspan
<INPUT size="20" type="text" maxlength="30" name="lastName"
valueproperty="user.lname" dynamicelement>--%>
<INPUT maxlength="30" name="lastName" size="20"
type="text" value="<%= user.getLname() %>">
<%--METADATA type="DynamicData" endspan--%></TD>
</TR>
<TR> |
<TD class="head">Edit User Information</TD>
........
<jsp:useBean id="user" class="com.goforit.user.UserDataBean" scope="session">
</jsp:useBean>
........
<TD class="subhead">Please update the following information as required:</TD>
........
<FORM action="/goforit/UpdateUserProfileServlet"
method="POST" onsubmit="return checkForm(this);">
........
<TD width="375" align="right">First Name:</TD>
<TD>
<%--METADATA type="DynamicData" startspan
<INPUT size="20" type="text" maxlength="25" name="firstName"
valueproperty="user.fname" dynamicelement>--%>
<INPUT maxlength="25" name="firstName" size="20"
type="text" value="<%= user.getFname() %>">
<%--METADATA type="DynamicData" endspan--%>
</TD>
</TR>
<TR>
<TD width="375" align="right">Last Name:</TD>
<TD>
<%--METADATA type="DynamicData" startspan
<INPUT size="20" type="text" maxlength="30" name="lastName"
valueproperty="user.lname" dynamicelement>--%>
<INPUT maxlength="30" name="lastName" size="20"
type="text" value="<%= user.getLname() %>">
<%--METADATA type="DynamicData" endspan--%></TD>
</TR>
<TR> |
Using client-side beans in our architecture helped us design our components independent of each other. They also turned out to be easy to implement, and come in very handy when we needed to pass data around the application. They help decouple our
application logic from the data, so when we add or change user fields all we need to do is change UserDataBean and the corresponding User EJB; none of the servlet logic needs to change. Using client-side beans allows for localized changes, and that's always a good thing.
Watch for our next installment of the Go-ForIt chronicles, where we'll cover View Beans, which offer a way to encapsulate a Web application's presentation logic. To see the previous articles in our tale of dragon slaying, go to our overview
- Participate in the discussion forum.
- Participate in the discussion forum on this article by clicking Discuss at the top or bottom of the article.
-
Explore IBM Patterns for e-business, pre-built designs for creating and implementing large integrated systems.
-
Review the User Stories (customer requirements) for the Go-ForIt application.
-
Refer to the JavaBeans specification from Sun Microsystems.
-
Read more about JavaBeans, including code examples, in
"JavaBeans: An architecture for reusable software components," by S. Narayanan in View Source Magazine
Sandeep Desai is an e-business Architect in IBM's Developer Relations Technical Consulting group in Austin, Texas. He works with IBM's Business partners -- small or large companies, from startups to large firms, helping them get excited, evangelized, educated, and enabled on IBM's e-business platform. Sandeep holds the following technical certifications, among others: IBM Certified e-business Solution Advisor, IBM Certified e-business Solution Designer, IBM Certified e-business Solution Technologist, IBM Certified Specialist -- WebSphere Application Server, IBM Certified Systems Expert -- WebSphere Application Server. You can reach him at sandeep@us.ibm.com
