You are on page 1of 220

PREFACE

This courseware is to help the participants to learn the basic principles of writing
applications for the subject. It surveys the different issues and discusses the
various techniques for dealing with it.

The participants have no doubt seen many books about the subject that are 800 to
1000 pages long. Presumably the participants have noticed by now that this
courseware is much smaller but we have tried to make the coverage as vast as
possible. It will always be our endeavor to improve the contents and coverage of
the material at all times.

The R&D team of professionals of TRENDZ has designed the courseware after
extensive research, taking into considerations the present market trends and the
requirements of the participants, keeping in mind the knowledge and
understanding level of the participants the language used in this book is very
simple and easy. This the third released version on the subject by the R&D team.

This is only an insight into the subject and the participants are advised to refer
the more sources to improve their technical knowledge. In daily sessions the
technical coordinator will discuss more real time application as covered in the
courseware.

The team to give the best knowledge through his courseware has put all efforts,
if you have any suggestion please advice on the same.

We would be appreciating the suggestions for the current courseware, please


contact the Technical Manager or mail to Director on ss@trendzit.net

No part or wholly of this publication may be reproduced, stored in retrieval


system or transmitted in any form, or by any means, electronic, mechanical,
recording or otherwise, without the prior written permission of the publisher.
This is a product developed by TRENDZ to circulate exclusively to its
participants.

All rights are reserved with Trendz


J2EE

INDEX

 Java Database Connectivity (JDBC)


o Overview
o Type of Drivers
o Connection & Statement
o Result Set
o Transactions

 Servlets
o Overview
o Life Cycle
o Request & Response
o HTTP Servlets
o Session Tracking

 Remote Method Invocation (RMI)


o Overview
o System Goals
o Interfaces & Classes

 Java Server Pages (JSP)


o Overview & Importance
o Web Application Development
o JSP Directives
o JSP Action Tags
o JSP Custom Tag Library

 Java 2 Enterprise Edition (J2EE)


o JNDI
 Naming & Directory Service
 Installing OpenLDAP
 Accessing Naming Service
 Accessing Directory Service
o Java Transaction API
 Transaction Service
 Bean managed Transaction
o Java Mail API
 Java Mail & JAF
 Sample Application
o EJB
 Overview
 Remote & Home Interfaces
 Entity Beans with CMP and BMP
 Session Bean as Stateless and Stateful
 EJB 2.0 Features
 EJB QL
 Message-Driven Bean
 EJB & WebService
o J2EE Design Pattern

© Trendz Information Technologies Ltd. Page No. 2 of 220


J2EE

 Model-view-Controller
 What is Design Pattern
 Helpful Hints

Overview

Types of Drivers

Connection & Statement

Result Set

Transactions

© Trendz Information Technologies Ltd. Page No. 3 of 220


J2EE

JDBC OVERVIEW

THE JDBC API:


The JDBC API provides programmatic access to relational data from the Java
programming language. Using the JDBC API, applications written in the Java
programming language can execute SQL statements, retrieve results, and propagate
changes back to an underlying data source. The JDBC API can also be used to
interact with multiple data sources in a distributed, heterogeneous environment.

The JDBC API is based on the X/Open SQL CLI, which is also the basis for ODBC.
JDBC provides a natural and easy-to-use mapping from the Java programming
language to the abstractions and concepts defined in the X/Open CLI and SQL
standards.

Since its introduction in January 1997, the JDBC API has become widely accepted
and implemented. The flexibility of the API allows for a broad range of
implementations.

PLATFORMS
The JDBC API is part of the Java platform, which includes the Java 2 Standard Edition
(J2SE) and the Java 2 Enterprise Edition (J2EE). The JDBC 3.0 API is divided into two
packages: java.sql and javax.sql. Both packages are included in the J2SE and J2EE
platforms.

GOALS:
The JDBC API is a mature technology, having first been specified in January 1997. In
its initial release, the JDBC API focused on providing a basic call-level interface to
SQL databases. The JDBC 2.1 specification and the 2.0 Optional Package
specifications then broadened the scope of the API to include support for more
advanced applications and for the features required by application servers to manage
use of the JDBC API on behalf of their applications. The overall goal of the JDBC 3.0
specification is to “round out” the API by filling in smaller areas of missing
functionality. The following list outlines the goals and design philosophy for the JDBC
API in general and the JDBC 3.0 API in particular:

 Fit into the J2EE and J2SE platforms


The JDBC API is a constituent technology of the Java platform. The JDBC 3.0
API should be aligned with the overall direction of the Java 2 Enterprise
Edition and Java 2 Standard Edition platforms.

 Be consistent with SQL99


The JDBC API provides programmatic access from applications written in the
Java programming language to standard SQL. At the time the JDBC 2.0 API
was in development, the SQL99 specification was a moving target. SQL99 is
now a published standard and includes features that are widely supported
among DBMS vendors as well as features that only a few vendors support.
The intent of the JDBC 3.0 API is to provide access to the subset of SQL99
features that are likely to be widely supported within the next five years.

© Trendz Information Technologies Ltd. Page No. 4 of 220


J2EE

 Consolidate predecessor specifications


This document incorporates content from three prior JDBC specifications to
provide a single standalone specification of the JDBC API.

 Offer vendor-neutral access to common features


The JDBC API strives to provide high-bandwidth access to features commonly
supported across different vendor implementations. The goal is to provide a
degree of feature access comparable to what can be achieved by native
applications. However, the API must be general and flexible enough to allow
for a wide range of implementations.

 Maintain the focus on SQL


The focus of the JDBC API has always been on accessing relational data from
the Java programming language. This continues to be true with the JDBC 3.0
API. The JDBC 3.0 API does not preclude interacting with other technologies,
including XML, CORBA, or non-relational data, but the primary target will still
be relational data and SQL.

 Provide a foundation for tools and higher-level APIs


The JDBC API presents a standard API to access a wide range of underlying
data sources or legacy systems. Implementation differences are made
transparent through JDBC API abstractions, making it a valuable target
platform for tools vendors who want to create portable tools and applications.
Because it is a “call-level” interface from the Java programming language to
SQL, the JDBC API is also suitable as a base layer for higher-level facilities
such as Enterprise Java Beans (EJB) 2.0 container-managed persistence and
SQL J.

 Keep it simple
The JDBC API is intended to be a simple-to-use, straightforward interface
upon which more complex entities can be built. This goal is achieved by
defining many compact, single-purpose methods instead of a smaller number
of complex, multipurpose ones with control flag parameters.

 Enhance reliability, availability, and scalability


Reliability, availability, and scalability are the themes of the J2EE and J2SE
platforms, as well as the direction for future Java platforms. The JDBC 3.0 API
stays true to these themes by enhancing support in several areas, including
resource management, the reuse of prepared statements across logical
connections, and error handling.

 Maintain backward compatibility with existing applications and


drivers
Existing JDBC technology-enabled drivers (“JDBC drivers”) and the
applications that use them must continue to work in an implementation of the
Java Virtual Machine that supports the JDBC 3.0 API. Applications that use
only features defined in earlier releases of the JDBC API, excluding those that
were deprecated by JDBC 2.0, will not require changes to continue running. It
should be straightforward for existing applications to migrate to JDBC 3.0
technology.

 Allow forward compatibility with Connectors

© Trendz Information Technologies Ltd. Page No. 5 of 220


J2EE

The Connector architecture defines a standard way to package and deploy a


resource adapter that allows a J2EE container to integrate its connection,
transaction, and security management with those of an external resource.

The JDBC 3.0 API provides the migration path for JDBC drivers to the
Connector architecture. It should be possible for vendors whose products use
JDBC technology to move incrementally towards implementing the Connector
API. The expectation is that these implementers will write “resource manager
wrappers” around their existing data source implementations so that they can
be reused in a Connector framework.

 Specify requirements unambiguously


The requirements for JDBC compliance need to be unambiguous and easy to
identify. The JDBC 3.0 specification and the API documentation (Javadoc
documentation) will clarify which features are required and which are
optional.

CONNECTIONS:
A Connection object represents a connection to a data source via a JDBC technology-
enabled driver. The data source can be a DBMS, a legacy file system, or some other
source of data with a corresponding JDBC driver. A single application using the JDBC
API may maintain multiple connections. These connections may access multiple data
sources, or they may all access a single data source.

From the JDBC driver perspective, a Connection object represents a client session. It
has associated state information such as user ID, a set of SQL statements and result
sets being used in that session, and what transaction semantics are in effect. To
obtain a connection, the application may interact with either:

 The DriverManager class working with one or more Driver implementations


(OR)
 A DataSource implementation

Using a DataSource object is the preferred method because it enhances application


portability, it makes code maintenance easier, and it makes it possible for an
application to transparently make use of connection pooling and distributed
transactions. All J2EE components that establish a connection to a data source use a
DataSource object to get connection.

It describes the various types of JDBC drivers and the use of the Driver interface, the
DriverManager class, and the basic DataSource interface. DataSource
implementations that support connection pooling and distributed transactions.

Types of Drivers
There are many possible implementations of JDBC drivers. These
implementations are categorized as follows:

 Type 1 — Drivers that implement the JDBC API as a mapping to another data
access API, such as ODBC. Drivers of this type are generally dependent on a
native library, which limits their portability. The JDBC-ODBC Bridge driver is
an example of a Type 1 driver.

© Trendz Information Technologies Ltd. Page No. 6 of 220


J2EE

 Type 2 — Drivers that are written partly in the Java programming language
and partly in native code. These drivers use a native client library specific to
the data source to which they connect. Again, because of the native code,
their portability is limited.
 Type 3 — Drivers that use a pure Java client and communicate with a
middleware server using a database-independent protocol. The middleware
server then communicates the client’s requests to the data source.
 Type 4 — Drivers that are pure Java and implement the network protocol for a
specific data source. The client connects directly to the data source.

The Driver Interface


JDBC drivers must implement the Driver interface, and the implementation must
contain a static initializer that will be called when the driver is loaded. This initializer
registers a new instance of itself with the DriverManager, as shown below.

public class AcmeJdbcDriver implements java.sql.Driver {


static {
java.sql.DriverManager.registerDriver(new AcmeJdbcDriver());
}
...
}

Static initializer for a driver implementing java.sql.Driver


Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

Loading a driver that implements java.sql.Driver


To insure that drivers can be loaded using this mechanism, drivers are required to
provide a constructor that takes no arguments.

The DriverManager class invokes Driver methods when it wishes to interact with a
registered driver. The Driver interface also includes the method accepts URL. The
DriverManager can use this method to determine which of its registered drivers it
should use for a given Universal Resource Locator (URL).

When the DriverManager is trying to establish a connection, it calls that driver’s


connect method and passes the driver, the URL. If the Driver implementation
understands the URL, it will return a Connection object; otherwise it returns null.

URL Syntax
The recommended JDBC URL syntax is structured as follows:

jdbc:<subprotocol>:<subname>

Where a ‘subprotocol’ names a particular kind of database connectivity


mechanism that may be supported by one or more drivers. The contents of the
‘subname’ will depend on the subprotocol.

The recommended syntax for a network address specified as part of a subname


follows the standard URL naming convention for the subname:

//hostname:port/subsubname

© Trendz Information Technologies Ltd. Page No. 7 of 220


J2EE

The subsubname can have arbitrary internal syntax.

Registering Subprotocol names


Sun Microsystems, Java Software Division, will act as an informal registry for JDBC
subprotocol names. Send mail to jdbc@eng.sun.com to reserve a subprotocol name.

DriverManager Class
The DriverManager class works with the Driver interface to manage the set of drivers
available to a JDBC client. When the client requests a connection and provides a URL,
the DriverManager is responsible for finding a driver that recognizes the URL and for
using it to connect to the corresponding data source.

Key DriverManager methods include:

 registerDriver() — this method adds a driver to the set of available drivers


and is invoked implicitly when the driver is loaded. The registerDriver()
method is typically called by the static initializer provided by each driver.
 getConnection() — the method the JDBC client invokes to establish a
connection. The invocation includes a JDBC URL, which the DriverManager
passes to each driver in its list until it finds one whose Driver.connect method
recognizes the URL. That driver returns a Connection object to the
DriverManager, which in turn passes it to the application.

illustrates how a JDBC client obtains a connection from the DriverManager.


// Load the driver. This creates an instance of the driver
// and calls the registerDriver method to make acme.db.Driver
// available to clients.

Class.forName("acme.db.Driver");

// Set up arguments for the call to the getConnection method.


// The sub-protocol "odbc" in the driver URL indicates the
// use of the JDBC-ODBC bridge.
String url = "jdbc:odbc:DSN";
String user = "SomeUser";
String passwd = "SomePwd";

// Get a connection from the first driver in the DriverManager


// list that recognizes the URL "jdbc:odbc:DSN".
Connection con = DriverManager.getConnection(url, user, passwd);

Loading a driver and getting a connection using the DriverManager:

The DriverManager class also provides two other getConnection() methods:

 getConnection(String url) for connecting to data sources that do not use a


username and password.
 getConnection(String url, java.util.Properties prop), which allows the client to
connect using a set of properties describing the user name and password
along with any addition information that may be required.

© Trendz Information Technologies Ltd. Page No. 8 of 220


J2EE

The DriverPropertyInfo class provides information on the properties that the JDBC
driver can understand.

SQLPermission Class
The SQLPermission class represents a set of permissions that a codebase may be
granted.

Currently the only permission defined is setLog. The SecurityManager will check for
the setLog permission when an Applet calls either the DriverManager method
setLogWriter or setLogStream. If the codebase does not have the setLog permission,
a java.lang.SecurityException exception will be thrown.

STATEMENTS
This section describes the Statement interface and its subclasses PreparedStatement
and CallableStatement. It also describes related topics, including escape syntax,
performance hints, and auto-generated keys.

 Statement Interface
The Statement interface defines methods for executing SQL statements that
do not contain parameter markers. The PreparedStatement interface adds
methods for setting input parameters, and the CallableStatement interface
adds methods for retrieving output parameter values returned from stored
procedures.
 Creating Statements
Statement objects are created by Connection object.

Connection conn = dataSource.getConnection(user, passwd);


Statement stmt = conn.createStatement()
Creating a Statement object

Each Connection object can create multiple Statement objects that may be
used concurrently by the program.

// get a connection from the DataSource object ds


Connection conn = ds.getConnection(user, passwd);
// create two instances of Statement
Statement stmt1 = conn.createStatement();
Statement stmt2 = conn.createStatement();

Creating multiple Statement objects from a single connection

 Setting ResultSet Characteristics


Additional constructors may be used to set the type and concurrency or
the type, concurrency,and hold ability of any result sets produced by a
statement.

It creates a Statement object that returns result sets that are scrollable, that
are insensitive to changes made while the ResultSet object is open, that can
be updated, and that do not close the ResultSet objects when a commit
operation is implicity or explicitly performed.

© Trendz Information Technologies Ltd. Page No. 9 of 220


J2EE

Connection conn = ds.getConnection(user, passwd);


Statement stmt = conn.createStatement(
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_UPDATABLE,
ResultSet.HOLD_CURSOR_OVER_COMMIT);

Creating a scrollable, insensitive, updatable result set that stays open after
the method commit is called ResultSet types.

 Executing Statement Objects


The method used to execute a Statement object depends on the type of SQL
statement being executed. If the Statement object represents an SQL query
returning a ResultSet object, the method executeQuery() should be used. If
the SQL is known to be a DDL statement or a DML statement returning an
update count; the method executeUpdate() should be used. If the type of the
SQL statement is not known, the method execute should be used.

 Returning a ResultSet object


shows the execution of an SQL string returning a ResultSet object.

Statement stmt = conn.createStatement();


ResultSet rs = stmt.executeQuery(“select TITLE, AUTHOR, ISBN " +
"from BOOKLIST”);
while (rs.next()){
...
}
Executing a Statement object that returns a ResultSet object If the SQL string
being executed does not return a ResultSet object, the method executeQuery
throws an SQLException.

 Returning an Update Count


The SQL statement being executed returns the number of rows affected by
the update.

Statement stmt = conn.createStatement();


int rows = stmt.executeUpdate(“update STOCK set ORDER = ‘Y’ " +
"where SUPPLY = 0”);
if (rows > 0) {
...
}
Executing a Statement object that returns an update count
The method executeUpdate() throws an SQLException if the SQL string being
executed does not return an update count.

 Using the Method execute


The method execute should be used only when the SQL string being executed
could return an update count, a ResultSet object, multiple ResultSet objects,
or the type of the statement is not known. The execute method returns true if
the first result is a ResultSet object and false if it is an update count.
Additional methods must be called to retrieve the ResultSet object or update
count or to retrieve additional results, if any.

© Trendz Information Technologies Ltd. Page No. 10 of 220


J2EE

String sql;
...

Statement stmt = conn.createStatement();


boolean b = stmt.execute(sql);
if (b == true) {
// b is true if a ResultSet is returned
ResultSet rs;
rs = stmt.getResultSet();
while (rs.next()) {
...
}
} else {
// b is false if an update count is returned
int rows = stmt.getUpdateCount();
if (rows > 0) {
...
}
}

Executing a Statement object that may return an update count or a ResultSet


object.

When the SQL string being executed returns a ResultSet object, the method
getUpdateCount() returns -1. If the SQL string being executed returns an
update count, the method getResultSet() returns null.

 Closing Statement Objects


An application calls the method Statement.close() to indicate that it has
finished processing a statement. All Statement objects will be closed when the
connection that created them is closed. However, it is good coding practice for
applications to close statements as soon as they have finished processing
them. This allows any external resources that the statement is using to be
released immediately.

Closing a Statement object will close and invalidate any instances of ResultSet
produced by that Statement object. The resources held by the ResultSet
object may not be released until garbage collection runs again, so it is a good
practice to explicitly close ResultSet objects when they are no longer needed.

These comments about closing Statement objects apply to


PreparedStatement and CallableStatement objects as well.

 PreparedStatement Interface
The PreparedStatement interface extends Statement, adding the ability to set
values for parameter markers contained within the statement.

PreparedStatement objects represent SQL statements that can be prepared,


or precompiled, for execution once and then executed multiple times.
Parameter markers, represented by “?” in the SQL string, are used to specify
input values to the statement that may vary at runtime.

 Creating a PreparedStatement Object

© Trendz Information Technologies Ltd. Page No. 11 of 220


J2EE

An instance of PreparedStatement is created in the same manner as a


Statement object, except that the SQL command is supplied when the
statement is created:

Connection conn = ds.getConnection(user, passwd);


PreparedStatement ps = conn.prepareStatement(“INSERT INTO BOOKLIST" +
"(AUTHOR, TITLE, ISBN) VALUES (?, ?, ?)”);

Creating a PreparedStatement object with three placeholder


Markers.

 Setting ResultSet Characteristics


As with createStatement(), the method prepareStatement() defines a
constructor that can be used to specify the characteristics of result sets
produced by that prepared statement.

Connection conn = ds.getConnection(user, passwd);


PreparedStatement ps = conn.prepareStatement(
“SELECT AUTHOR, TITLE FROM BOOKLIST WHERE ISBN = ?”,
ResultSet.TYPE_FORWARD_ONLY,ResultSet.CONCUR_UPDATABLE);

Creating a PreparedStatement object that returns forward only, updatable


result sets

 Setting Parameters
The Prepared Statement interface defines setter methods that are used to
substitute values for each of the parameter markers in the precompiled SQL
string. The names of the methods follow the pattern "set<Type>".

For example, the method setString() is used to specify a value for a


parameter marker that expects a string. Each of these setter methods takes
at least two parameters. The first is always an int equal to the ordinal position
of the parameter to be set, starting at 1. The second and any remaining
parameters specify the value to be assigned to the parameter.

PreparedStatement ps = conn.prepareStatement(“INSERT INTO BOOKLIST"


+"(AUTHOR, TITLE, ISBN) VALUES (?, ?, ?)”);
ps.setString(1, “Zamiatin, Evgenii”);
ps.setString(2, “We”);
ps.setLong(3, 0140185852);

Setting parameters in a PreparedStatement object :

A value must be provided for each parameter marker in the


PreparedStatement object before it can be executed. The methods used to
execute a PreparedStatement object (executeQuery, executeUpdate and
execute) will throw an SQLException if a value is not supplied for a parameter
marker.

The values set for the parameter markers of a PreparedStatement object are
not reset when it is executed. The method clearParameters() can be called to

© Trendz Information Technologies Ltd. Page No. 12 of 220


J2EE

explicitly clear the values that have been set. Setting a parameter with a
different value will replace the previous value with the new one.

 Type Conversions
The data type specified in a PreparedStatement setter method is a data type
in the Java programming language. The JDBC driver is responsible for
mapping this to the corresponding JDBC type (one of the SQL types defined in
java.sql.Types) so that it is the appropriate type to be sent to the data source.

 Type Conversions Using the Method setObject


The method setObject() can be used to convert an object in the Java
programming language to a JDBC type.

The conversion is explicit when setObject() is passed a Java Object and a


JDBC data type. The driver will attempt to convert the Object to the specified
JDBC type before passing it to the data source. If the object cannot be
converted to the target type, an SQLException object is thrown. A Java Object
of type Integer is being converted to the JDBC type SHORT.

Integer value = new Integer(15);


ps.setObject(1, value, java.sql.Types.SHORT);

Converting an Integer object to an SQL SHORT:


If setObject() is called without a type parameter, the Java Object is implicitly
mapped using the default mapping for that object type.

Integer value = new Integer(15);


// value is mapped to java.sql.Types.INTEGER
ps.setObject(1, value);

 Setting NULL Parameters


The method setNull() can be used to set any parameter to JDBC NULL. It
takes two parameters, the ordinal position of the parameter marker, and the
JDBC type of the parameter.

ps.setNull(2, java.sql.Types.VARCHAR);

Setting a String parameter to JDBC NULL:


If a Java null is passed to any of the setter methods that take a Java object,
the parameter will be set to JDBC NULL.

 Describing Outputs and Inputs of a PreparedStatement Object The method


PreparedStatement.getMetaData() retrieves a ResultSetMetaData object
containing a description of the columns that will be returned by a prepared
statement when is it executed. The ResultSetMetaData object contains a
record for each column being returned. Methods in the ResultSetMetaData
interface provide information about the number of columns being returned
and the characteristics of each column.

PreparedStatement pstmt = conn.prepareStatement(


"SELECT * FROM CATALOG");
ResultSetMetaData rsmd = pstmt.getMetaData();
int colCount = rsmd.getColumnCount();

© Trendz Information Technologies Ltd. Page No. 13 of 220


J2EE

int colType;
String colLabel;
for (int i = 1; i <= colCount; i++) {
colType = rsmd.getColumnType(i);
colLabel = rsmd.getColumnLabel(i);
...
}

Creating a ResultSetMetaData object, retrieving column information:


The method PreparedStatement.getParameterMetaData() returns a
ParameterMetaData object describing the parameter markers that appear in
the PreparedStatement object. Methods in the ParameterMetaData interface
provide information about the number of parameters and their characteristics.

PreparedStatement pstmt = conn.prepareStatement(


"SELECT * FROM BOOKLIST WHERE ISBN = ?");
...
ParameterMetaData pmd = pstmt.getParameterMetaData();
int colType = pmd.getParameterType(1);
...

 Executing a PreparedStatement Object


As with Statement objects, the method used to execute a PreparedStatement
object depends on the type of SQL statement being executed. If the
PreparedStatement object is a query returning a ResultSet object, it should be
executed with the method executeQuery(). If it is a DML statement returning
a row count, it should be executed with the method executeUpdate(). The
method execute should be used only if the return type of the statement is
unknown. If any of the PreparedStatement execute methods is called with an
SQL string as a parameter, an SQLException is thrown.

 Returning a ResultSet Object Shows a query being prepared and then


executed multiple times.

PreparedStatement pstmt = conn.prepareStatement(“SELECT AUTHOR, " +


"TITLE FROM BOOKLIST WHERE SECTION = ?”);
for (int i = 1; i <= maxSectionNumber; i++) {
pstmt.setInt(1, i);
ResultSet rs = pstmt.executeQuery();
while (rs.next()) {
// process the record
}
rs.close();
}
pstmt.close();

If the statement being executed does not return a ResultSet object, the
method executeQuery() throws an SQLException.

 Returning a Row Count

© Trendz Information Technologies Ltd. Page No. 14 of 220


J2EE

If the statement being prepared and executed is a DML or DDL operation, it


should be executed using the method executeUpdate(). This method returns
the number of rows that the statement affected.

PreparedStatement pstmt = conn.prepare(


“update stock set reorder = ’Y’ where stock < ?”);
pstmt.setInt(1, 5);
int num = pstmt.executeUpdate();

If the statement being executed returns a ResultSet object, an SQLException


is thrown.

 Using the Method execute


If the return type of a PreparedStatement object is not known or may return
multiple ResultSet objects, it should be executed with the execute method. As
is true with Statement objects, the methods getResultSet() and
getUpdateCount() can be used to retrieve a result.

PreparedStatement pstmt = conn.prepareStatement(sqlStatement);


// set any parameters the user passes
boolean b = pstmt.execute();
if (b == true) {
ResultSet rs = pstmt.getResultSet();
// process a ResultSet
...
}
} else {
int rowCount = pstmt.getUpdateCount();
// process row count
...
}
}

CallableStatement Interface
The CallableStatement interface extends PreparedStatement with methods for
executing and retrieving results from stored procedures.

 Creating a CallableStatement Object


As with Statement and PreparedStatement objects, CallableStatement objects
are created by Connection objects. It shows the creation of a
CallableStatement object for calling the stored procedure ‘validate’, which has
a return parameter and two other parameters.

CallableStatement cstmt =
conn.prepareCall(“{? = call validate(?, ?)}”);

Creating a CallableStatement object

 Setting Parameters
Callable Statement objects may take three types of parameters: IN, OUT, and
INOUT. The parameter can be specified as either an ordinal parameter or a

© Trendz Information Technologies Ltd. Page No. 15 of 220


J2EE

named parameter. A value must be set for each parameter marker in the
statement.

The number, type, and attributes of parameters to a stored procedure can be


determined using the DatabaseMetaData method getProcedureColumns().
Parameter ordinals, which are integers passed to the appropriate setter
method, refer to the parameter markers ("?") in the statement, starting at
one. Literal parameter values in the statement do not increment the ordinal
value of the parameter markers.

the two parameter markers have the ordinal values 1 & 2.

CallableStatement cstmt = con.prepareCall(


"{CALL PROC(?, "Literal_Value", ?)}");
cstmt.setString(1, "First");
cstmt.setString(2, "Third");

Specifying ordinal parameters


Named parameters can also be used to specify specific parameters. This is
especially useful when a procedure has many parameters with default values.
Named parameters can be used to specify only the values that have no
default value. The name of a parameter corresponds to the COLUMN_NAME
field returned by DatabaseMetaData.getProcedureColumns().

CallableStatement cstmt = con.prepareCall(


"{CALL COMPLEX_PROC(?, ?)}";
cstmt.setString("PARAM_1", "Price");
cstmt.setFloat("PARAM_5", 150.25);

The DatabaseMetaData.supportsNamedParameters() method can be called to


determine if a JDBC driver and underlying data source support specifying
named parameters.

It is not possible to combine setting parameters with ordinals and with names
in the same statement. If ordinals and names are used for parameters in the
same statement, an SQLException is thrown.

Note: In some cases it may not be possible to provide only some of the
parameters for a procedure. For example, if the procedure name is
overloaded, the data source determines which procedure to call based on the
number of parameters. Enough parameters must be provided to allow the
data source to resolve any ambiguity.

 IN Parameters
IN parameters are assigned values using the setter methods.

cstmt.setString(1, “October”);
cstmt.setDate(2, date);

 OUT Parameters
The method registerOutParameter() must be called to set the type for each
OUT parameter before a CallableStatement object is executed. When the

© Trendz Information Technologies Ltd. Page No. 16 of 220


J2EE

stored procedure returns from execution, it will use these types to set the
values for any OUT parameters.

The values of OUT parameters can be retrieved using the appropriate getter
methods defined in the CallableStatement interface. The following lines shows
the execution of a stored procedure with two OUT parameters, a string and
float, and the retrieval of the OUT parameter values.

CallableStatement cstmt = conn.prepareCall(


“{CALL GET_NAME_AND_NUMBER(?, ?)}");
cstmt.registerOutParameter(1, java.sql.Types.STRING);
cstmt.registerOutParameter(2, java.sql.Types.FLOAT);
cstmt.execute();
// Retrieve OUT parameters
String name = cstmt.getString(1);
float number = cstmt.getFloat(2);

 INOUT Parameters
Parameters that are both input and output parameters must be both set by
using the appropriate setter method and also registered by calling the
registerOutParameter() method. The type implied by the setter method and
the type supplied to the method registerOutParameter() must be the same.

CallableStatement cstmt = conn.prepareCall(“{CALL CALC(?)}”);


cstmt.setFloat(1, 1237.98f);
ctsmt.registerOutParameter(1, java.sql.Types.FLOAT);
cstmt.execute();
float f = cstmt.getFloat(1);

 Executing a CallableStatement Object


As with Statement and PreparedStatement objects, the method used to
execute a CallableStatement object depends on whether it returns a single
ResultSet object, an update count, or multiple mixed results.

RESULT SETS
The ResultSet interface provides methods for retrieving and manipulating the results
of executed queries.

 Kinds of ResultSet Objects


ResultSet objects can have different functionality and characteristics. These
characteristics are result set type, result set concurrency, and cursor
holdability.

 ResultSet Types
The type of a ResultSet object determines the level of its functionality in two
main areas: (1) the ways in which the cursor can be manipulated, (2) how
concurrent changes made to the underlying data source are reflected by the
ResultSet object. The latter is called the sensitivity of the ResultSet object.

The three different ResultSet types are described below.

1. TYPE_FORWARD_ONLY

© Trendz Information Technologies Ltd. Page No. 17 of 220


J2EE

• The result set is not scrollable; its cursor moves forward only, from
before the first row to after the last row.
• The rows contained in the result set depend on how the underlying
database materializes the results. That is, it contains the rows that
satisfy the query at either the time the query is executed or as the
rows are retrieved.

2. TYPE_SCROLL_INSENSITIVE
• The result set is scrollable; its cursor can move both forward and
backward relative to the current position, and it can move to an
absolute position.
• The result set is insensitive to changes made to the underlying data
source while it is open. It contains the rows that satisfy the query at
either the time the query is executed or as the rows are retrieved.

3. TYPE_SCROLL_SENSITIVE
• The result set is scrollable; its cursor can move both forward and
backward relative to the current position, and it can move to an
absolute position.
• The result set reflects changes made to the underlying data source
while the result set remains open.

NOTE: The default ResultSet type is TYPE_FORWARD_ONLY.

The method DatabaseMetaData.supportsResultSetType() returns true if the


specified type is supported by the driver and false otherwise.

If the driver does not support the type supplied to the methods
createStatement(), prepareStatement(), or prepareCall(), it generates an
SQLWarning on the Connection object that is creating the statement. When
the statement is executed, the driver returns a ResultSet object of a type that
most closely matches the requested type. An application can find out the type
of a ResultSet object by calling the method ResultSet.getType().

 ResultSet Concurrency
The concurrency of a ResultSet object determines what level of update
functionality is supported.

The two concurrency levels are:


• CONCUR_READ_ONLY
The ResultSet object cannot be updated using the ResultSet interface.
• CONCUR_UPDATABLE
The ResultSet object can be updated using the ResultSet interface.

NOTE: The default ResultSet concurrency is CONCUR_READ_ONLY.

The method DatabaseMetaData.supportsResultSetConcurrency() returns true


if the specified concurrency level is supported by the driver and false
otherwise.

If the driver does not support the concurrency level supplied to the methods
createStatement(), prepareStatement(), or prepareCall(), it generates an

© Trendz Information Technologies Ltd. Page No. 18 of 220


J2EE

SQLWarning on the Connection object that is creating the statement. An


application can find out the concurrency of a ResultSet object by calling the
method ResultSet.getConcurrency().

If the driver cannot return a ResultSet object at the requested type and
concurrency, it determines the appropriate type before determining the
concurrency.

 ResultSet Holdability
Calling the method Connection.commit() can close the ResultSet objects that
have been created during the current transaction. In some cases, however,
this may not be the desired behaviour. The ResultSet property holdability
gives the application control over whether ResultSet objects (cursors) are
closed when a commit operation is implicitly or explicitly performed.

The following ResultSet constants may be supplied to the Connection methods


createStatement(), prepareStatement(), and prepareCall():

1.HOLD_CURSORS_OVER_COMMIT
• ResultSet objects (cursors) are not closed; they are held open when a
commit operation is implicity or explicity performed.
2.CLOSE_CURSORS_AT_COMMIT
• ResultSet objects (cursors) are closed when a commit operation is
implicitly or explicitly performed. Closing cursors at commit can result
in better performance for some applications.

The default holdability of ResultSet objects is implementation defined. The


DatabaseMetaData method getResultSetHoldability() can be called to
determine the default holdability of result sets returned by the underlying
data source.

 ResultSet Type, Concurrency and Holdability


The parameters supplied to the methods Connection.createStatement(),
Connection.prepareStatement(), and Connection.prepareCall() determine the
type, concurrency, and holdability of ResultSet objects that the statement
produces. The following statements creates a Statement object that will
return scrollable, read-only ResultSet objects that are insensitive to updates
made to the data source and that will be closed when the transaction in which
they were created is committed.

Connection conn = ds.getConnection(user, passwd);


Statement stmt = conn.createStatement(
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY,
ResultSet.CLOSE_CURSORS_AT_COMMIT);

Creating a scrollable, insensitive, read-only result set with a cursor that is not
holdable.

The Statement, PreparedStatement and CallableStatement interfaces also


provide setter and getter methods for each of these properties.

© Trendz Information Technologies Ltd. Page No. 19 of 220


J2EE

Creating and Manipulating ResultSet Objects

 Creating ResultSet Objects


A ResultSet object is most often created as the result of executing a
Statement object. The Statement methods executeQuery() and
getResultSet() both return a ResultSet object, as do various
DatabaseMetaData methods.

Statement stmt = conn.createStatement();


ResultSet rs = stmt.executeQuery(“select author, title, isbn " +
"from booklist”);

For each book in the table booklist, the ResultSet object will contain a row
consisting of three columns, author, title, and isbn. The following sections
detail how these rows and columns can be retrieved.

 Cursor Movement
A ResultSet object maintains a cursor, which points to its current row of data.
When a ResultSet object is first created, the cursor is positioned before the
first row. The following methods can be used to move the cursor:
• next() — moves the cursor forward one row. Returns true if the cursor
is now positioned on a row and false if the cursor is positioned after
the last row.
• previous() — moves the cursor backwards one row. Returns true if
the cursor is now positioned on a row and false if the cursor is
positioned before the first row.
• first() — moves the cursor to the first row in the ResultSet object.
Returns true if the cursor is now positioned on the first row and false if
the ResultSet object does not contain any rows.
• last() — moves the cursor to the last row in the ResultSet object.
Returns true if the cursor is now positioned on the last row and false if
the ResultSet object does not contain any rows.
• beforeFirst() — positions the cursor at the start of the ResultSet
object, before the first row. If the ResultSet object does not contain
any rows, this method has no effect.
• afterLast() — positions the cursor at the end of the ResultSet object,
after the last row. If the ResultSet object does not contain any rows,
this method has no effect.
• relative(int row)— moves the cursor relative to its current position.
If row is 0 (zero), the cursor is unchanged. If row is positive, the
cursor is moved forward row rows. If the cursor is less than the
specified number of rows from the last row, the cursor is positioned
after the last row. If row is negative, the cursor is moved backward
row rows. If the cursor is less than row rows from the first row, the
cursor is positioned before the first row.

The method relative returns true if the cursor is positioned on a valid


row and false otherwise.

If row is 1, relative is identical to the method next. If rows is -1,


relative is identical to the method previous.

© Trendz Information Technologies Ltd. Page No. 20 of 220


J2EE

• absolute(int row) — positions the cursor on the row-th row of the


ResultSet object.

If row is positive, the cursor is moved row rows from the beginning of
the ResultSet object. The first row is 1, the second 2, and so on. If row
is greater than the number of rows in the ResultSet object, the cursor
is positioned after the last row.

If row is negative, the cursor is moved row rows from the end of the
ResultSet object. The last row is -1, the penultimate -2, and so on. If
row is greater than the number of rows in the ResultSet object, the
cursor is positioned before the first row.

Calling absolute(0) moves the cursor before the first row.

For a ResultSet object that is of type TYPE_FORWARD_ONLY, the only valid


cursor movement method is next. All other cursor movement methods throw
an SQLException.

 Retrieving Values
The ResultSet interface provides methods for retrieving the values of columns
from the row where the cursor is currently positioned.

Two getter methods exist for each JDBC type: one that takes the column
index as its first parameter and one that takes the column name or label.

The columns are numbered from left to right, as they appear in the select list
of the query, starting at 1.

Column names supplied to getter methods are case insensitive. If a select list
contains the same column more than once, the first instance of the column
will be returned.

The index of the first instance of a column name can be retrieved using the
method findColumn(). If the specified column is not found, the method
findColumn() throws an SQLException.

ResultSet rs = stmt.executeQuery(sqlstring);
int colIdx = rs.findColumn(“ISBN”);

 ResultSetMetadata
When the ResultSet method getMetaData is called on a ResultSet object, it
returns a ResultSetMetaData object describing the columns of that ResultSet
object. In cases where the SQL statement being executed is unknown until
runtime, the ResultSetMetaData can be used to determine which of the getter
methods should be used to retrieve the data.

ResultSet rs = stmt.executeQuery(sqlString);
ResultSetMetaData rsmd = rs.getMetaData();
int colType [] = new int[rsmd.getColumnCount()];
for (int idx = 0, int col = 1; idx < colType.length; idx++, col++)
colType[idx] = rsmd.getColumnType(col);

© Trendz Information Technologies Ltd. Page No. 21 of 220


J2EE

 Retrieving NULL values


The method wasNull() can be called to determine if the last value retrieved
was a JDBC NULL in the database.

When the column value in the database is JDBC NULL, it may be returned to
the Java application as null, 0, or false, depending on the type of the column
value. Column values that map to Java Object types are returned as a Java
null; those that map to numeric types are returned as 0; those that map to a
Java Boolean are returned as false. Therefore, it may be necessary to call the
wasNull() method to determine whether the last value retrieved was a JDBC
NULL.

 Modifying ResultSet Objects


ResultSet objects with concurrency CONCUR_UPDATABLE can be updated
using ResultSet methods. Columns can be updated, new rows can be
inserted, and rows can be deleted using methods defined in the ResultSet
interface.

 Updating a Row
Updating a row in a ResultSet object is a two-phase process. First, the new
value for each column being updated is set, and then the change is applied to
the row. The row in the underlying data source is not updated until the
second phase is completed.

The ResultSet interface contains two update methods for each JDBC type, one
specifying the column to be updated as an index and one specifying the
column name as it appears in the select list.

Column names supplied to updater methods are case insensitive. If a select


list contains the same column more than once, the first instance of the
column will be updated.

The method updateRow() is used to apply all column changes to the current
row. The changes are not made to the row until updateRow() has been called.
The method cancelUpdates() can be used to back out changes made to the
row before the method updateRow() is called. The Following code shows the
current row being updated to change the value of the column “author” to
“Karnitkar, Yaswant”:

Statement stmt = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY,


ResultSet.CONCUR_UPDATABLE);
ResultSet rs = stmt.executeQuery(“select author from booklist " +
"where isbn = 140185852”);
rs.next();
rs.updateString(“author”, “Yaswant, Karnitkar”);
rs.updateRow();

The method DatabaseMetaData.ownUpdatesAreVisible(int type) returns true if


a ResultSet object of the specified type is able to see its own updates and
false otherwise.

© Trendz Information Technologies Ltd. Page No. 22 of 220


J2EE

A ResultSet object may be able to use the method rowUpdated to detect rows
that have had the method updateRow called on them. The method
DatabaseMetaData.updatesAreDetected(int type) returns true if a ResultSet
object of the specified type can determine if a row is updated using the
method rowUpdated() and false otherwise.

 Deleting a Row
A row in a ResultSet object can be deleted using the method deleteRow().
ResultSet rows being deleted.

rs.absolute(4);
rs.deleteRow();

 Inserting a Row
New rows may be inserted using the ResultSet interface. New rows are
constructed in a special insert row. The steps to insert a new row are:

1. Move the cursor to the insert row


2. Set the values for the columns of the row using the ResultSet interface
update methods
3. Insert the new row into the ResultSet object

The Following steps necessary to insert a new row into the table booklist.

// select all the columns from the table booklist


ResultSet rs = stmt.executeQuery(“select author, title, isbn " +
"from booklist”);
rs.moveToInsertRow();
// set values for each column
rs.updateString(1, “Huxley, Aldous”);
rs.updateString(2, “Doors of Perception and Heaven and Hell”);
rs.updateLong(3, 60900075);
// insert the row
rs.insertRow();
// move the cursor back to its position in the result set
rs.moveToCurrentRow();

Each column in the insert row that does not allow null as a value and does not
have a default value must be given a value using the appropriate update
method. If this is not the case, the method insertRow() will throw an
SQLException.

 Closing a ResultSet Object


A ResultSet object is automatically closed when the Statement object that
produced it is closed. The method close can be called explicitly to close a
ResultSet object, thereby releasing any external resources and making it
immediately available for garbage collection.

TRANSACTIONS
Transactions are used to provide data integrity, correct application semantics,
and a consistent view of data during concurrent access. All JDBC compliant

© Trendz Information Technologies Ltd. Page No. 23 of 220


J2EE

drivers are required to provide transaction support. Transaction management in


the JDBC API mirrors the SQL99 specification and includes these concepts:

 Auto-commit mode
 Transaction isolation levels
 SavePoints

It describes transaction semantics associated with a single Connection object.

 Transaction Boundaries and Autocommit


When to start a new transaction is a decision made implicitly by either the
JDBC driver or the underlying data source. Although some data sources
implement an explicit “begin transaction” statement, there is no JDBC API to
do so. Typically, a new transaction is started when the current SQL statement
requires one and there is no transaction already in place. Whether or not a
given SQL statement requires a transaction is also specified by SQL99.

The Connection attribute auto-commit specifies when to end transactions.


Enabling auto-commit causes the JDBC driver to do a transaction commit
after each individual SQL statement as soon as it is complete. The point at
which a statement is considered to be “complete” depends on the type of SQL
statement as well as what the application does after executing it:

• For Insert, Update, Delete, and DDL statements, the statement is


complete as soon as it has finished executing.
• For Select statements, the statement is complete when the associated
result set is closed. The result set is closed as soon as one of the
following occurs:
 All of the rows have been retrieved
 The associated Statement object is re-executed
 Another Statement object is executed on the same connection
• For CallableStatement objects, the statement is complete when all of
the associated result sets have been closed.

 Disabling Auto-commit Mode

// Assume con is a Connection object


con.setAutoCommit(false);

When auto-commit is disabled, each transaction must be explicitly committed


by calling the Connection method commit or else explicitly rolled back by
calling the Connection method rollback. This is appropriate for cases where
transaction management is being done in a layer above the driver such as:
• When the application needs to group multiple SQL statements into a
single transaction
• When the transaction is being managed by the application server

The default is for auto-commit mode to be enabled when the Connection


object is created. If the value of auto-commit is changed in the middle of a
transaction, the current transaction is committed. It is an error to enable
auto-commit for a connection participating in a distributed transaction.

© Trendz Information Technologies Ltd. Page No. 24 of 220


J2EE

 Transaction Isolation Levels


Transaction isolation levels specify what data is “visible” to the statements
within a transaction. They directly impact the level of concurrent access by
defining what interaction, if any, is possible between transactions against the
same target data source. Possible interaction between concurrent transactions
is categorized as follows:

• Dirty reads occur when transactions are allowed to see uncommitted


changes to the data. In other words, changes made inside a
transaction are visible outside the transaction before they are
committed. If the changes are rolled back instead of being committed,
it is possible for other transactions to have done work based on
incorrect, transient data.
• Nonrepeatable reads occur when:
 Transaction A reads a row.
 Transaction B changes the row.
 Transaction A reads the same row a second time and gets
different results.
• Phantom reads occur when:
 Transaction A reads all rows that satisfy a WHERE condition
 Transaction B inserts an additional row that satisfies the same
condition.
 Transaction A reevaluates the WHERE condition and picks up
the additional “phantom” row.

JDBC augments the four levels of transaction isolation defined by SQL99, by


adding TRANSACTION_NONE. From least restrictive to most restrictive, the
transaction isolation levels are:

1. TRANSACTION_NONE — indicates that the driver does not support


transactions, which means that it is not a JDBC compliant driver.
2. TRANSACTION_READ_UNCOMMITTED — allows transactions to see
uncommitted changes to the data. This means that dirty reads, non-
repeatable reads, and phantom reads are possible.
3. TRANSACTION_READ_COMMITTED — means that any changes made
inside a transaction are not visible outside the transaction until the
transaction is committed. This prevents dirty reads, but non-repeatable reads
and phantom reads are still possible.
4. TRANSACTION_REPEATABLE_READ — disallows dirty reads and non-
repeatable reads. Phantom read are still possible.
5. TRANSACTION_SERIALIZABLE — specifies that dirty reads, non-
repeatable reads, and phantom reads are prevented.

 Using the setTransactionIsolation() Method


The default transaction level for a Connection object is determined by the
driver supplying the connection. Typically, it is the default transaction level
supported by the underlying data source.

The Connection method setTransactionIsolation() is provided to allow clients


to change the transaction isolation level for a given Connection object. The

© Trendz Information Technologies Ltd. Page No. 25 of 220


J2EE

new isolation level remains in effect for the remainder of the session or until
the next invocation of the setTransactionIsolation() method.

The result of invoking the method setTransactionIsolation() in the middle of a


transaction is implementation-defined.

The return value of the method getTransactionIsolation() should reflect the


change in isolation level when it actually occurs. It is recommended that
drivers implement the setTransactionIsolation() method to change the
isolation level starting with the next transaction. Committing the current
transaction to make the effect immediate is also a valid implementation.

It is possible for a given JDBC driver to not support all four-transaction


isolation levels (not counting TRANSACTION_NONE). If a driver does not
support the isolation level specified in an invocation of
setTransactionIsolation(), it is allowed to substitute a higher, more restrictive
transaction isolation level. If a driver is unable to substitute a higher
transaction level, it throws an SQLException. The DatabaseMetaData method
supportsTransactionIsolationLevel() may be used to determine whether or not
the driver supports a given level.

 Performance Considerations
As the transaction isolation level increases, more locking and other DBMS
overhead is required to ensure the correct semantics. This in turn lowers the
degree of concurrent access that can be supported. As a result, applications
may see decreased performance when they use a higher transaction isolation
level. For this reason, the transaction manager, whether it is the application
itself or part of the application server, should weigh the need for data
consistency against the requirements for performance when determining
which transaction isolation level is appropriate.

 Savepoints
Savepoints provide finer-grained control of transactions by marking
intermediate points within a transaction. Once a savepoint has been set, the
transaction can be rolled back to that savepoint without affecting preceding
work. The DatabaseMetaData.supportsSavepoints method can be used to
determine whether a JDBC API implementation supports savepoints.

 Setting and Rolling Back to a Savepoint


The JDBC 3.0 API adds the method Connection.setSavepoint(), which sets a
savepoint within the current transaction. The Connection.rollback() method
has been overloaded to take a savepoint argument.

The following code inserts a row into a table, sets the savepoint svpt1, and
then inserts a second row. When the transaction is later rolled back to svpt1,
the second insertion is undone, but the first insertion remains intact. In other
words, when the transaction is committed, only the row containing ’FIRST’ will
be added to TAB1.
Statement stmt = conn.createStatement();
int rows = stmt.executeUpdate("INSERT INTO TAB1 (COL1) VALUES "
+"(’FIRST’)");
// set savepoint
Savepoint svpt1 = conn.setSavepoint("SAVEPOINT_1");

© Trendz Information Technologies Ltd. Page No. 26 of 220


J2EE

rows = stmt.executeUpdate("INSERT INTO TAB1 (COL1) " +


"VALUES (’SECOND’)");
...
conn.rollback(svpt1);
...
conn.commit();

 Releasing a Savepoint
The method Connection.releaseSavepoint takes a Savepoint object as a
parameter and removes it from the current transaction.

Once a savepoint has been released, attempting to reference it in a rollback


operation will cause an SQLException to be thrown.

Any savepoints that have been created in a transaction are automatically


released and become invalid when the transaction is committed or when the
entire transaction is rolled back.

Rolling a transaction back to a savepoint automatically releases and makes


invalid any other savepoints that were created after the savepoint in question.

© Trendz Information Technologies Ltd. Page No. 27 of 220


J2EE

Overview

Life Cycle

Request & Response

HTTP Servlets

Session Tracking

© Trendz Information Technologies Ltd. Page No. 28 of 220


J2EE

SERVLETS OVERVIEW

What is a Servlet?
A Servlet is a Java technology-based Web component, managed by a container that
generates dynamic content. Like other Java technology-based components, Servlets
are platform-independent Java classes that are compiled to platform-neutral byte
code that can be loaded dynamically into and run by a Java technology-enabled Web
server. Containers, sometimes called Servlet engines, are Web server extensions that
provide Servlet functionality. Servlets interact with Web clients via a
request/response paradigm implemented by the Servlet container.

What is a Servlet Container?


The Servlet container is a part of a Web server or application server that provides the
network services over which requests and responses are sent, decodes MIME-based
requests, and formats MIME-based responses. A Servlet container also contains and
manages Servlets through their lifecycle. A Servlet container can be built into a host
Web server, or installed as an add-on component to a Web Server via that server’s
native extension API. Servlet containers can also be built into or possibly installed
into Web-enabled application servers. All Servlet containers must support HTTP as a
protocol for requests and responses, but additional request/response-based protocols
such as HTTPS (HTTP over SSL) may be supported. The required versions of the
HTTP specification that a container must implement are HTTP/1.0 and HTTP/1.1.
Because the container may have a caching mechanism described in RFC2616
(HTTP/1.1), it may modify requests from the clients before delivering them to the
Servlet, may modify responses produced by Servlets before sending them to the
clients, or may respond to requests without delivering them to the Servlet under the
compliance with RFC2616.

A Servlet container may place security restrictions on the environment in which a


Servlet executes. In a Java 2 Platform, Standard Edition or Java 2 Platform,
Enterprise Edition environment, these restrictions should be placed using the
permission architecture defined by the Java 2 platform. For example, high-end
application servers may limit the creation of a Thread object to insure that other
components of the container are not negatively impacted.

J2SE is the minimum version of the underlying Java platform with which Servlet
containers must be built.

An Example
The following is a typical sequence of events:

1. A client (e.g., a Web browser) accesses a Web server and makes an HTTP
request.
2. The request is received by the Web server and handed off to the Servlet
container. The Servlet container can be running in the same process as the
host Web server, in a different process on the same host, or on a different
host from the Web server for which it processes requests.
3. The Servlet container determines which Servlet to invoke based on the
configuration of its Servlets, and calls it with objects representing the request
and response.

© Trendz Information Technologies Ltd. Page No. 29 of 220


J2EE

4. The Servlet uses the request object to find out who the remote user is, what
HTTP POST parameters may have been sent as part of this request, and other
relevant data. The Servlet performs whatever logic it was programmed with,
and generates data to send back to the client. It sends this data back to the
client via the response object.
5. Once the Servlet has finished processing the request, the Servlet container
ensures that the response is properly flushed, and returns control back to the
host Web server.

Comparing Servlets with Other Technologies


In functionality, Servlets lie somewhere between Common Gateway Interface (CGI)
programs and proprietary server extensions such as the Netscape Server API
(NSAPI) or Apache Modules.

Servlets have the following advantages over other server extension mechanisms:

• They are generally much faster than CGI scripts because a different process
model is used.
• They use a standard API that is supported by many Web servers.
• They have all the advantages of the Java programming language, including
ease of development and platform independence.
• They can access the large set of APIs available for the Java platform.

Servlet Interface:
The Servlet interface is the central abstraction of the Java Servlet API. All Servlets
implement this interface either directly, or more commonly, by extending a class that
implements the interface. The two classes in the Java Servlet API that implement the
Servlet interface are GenericServlet and HttpServlet. For most purposes, Developers
will extend HttpServlet to implement their Servlets.

Request Handling Methods


The basic Servlet interface defines a service method for handling client requests. This
method is called for each request that the Servlet container routes to an instance of
a Servlet.

The handling of concurrent requests to a Web application generally requires that the
Web Developer design Servlets that can deal with multiple threads executing within
the service method at a particular time.

Generally the Web container handles concurrent requests to the same Servlet by
concurrent execution of the service method on different threads.

HTTP Specific Request Handling Methods


The HttpServlet abstract subclass adds additional methods beyond the basic Servlet
interface that are automatically called by the service method in the HttpServlet class
to aid in processing HTTP-based requests. These methods are:

• doGet() for handling HTTP GET requests


• doPost() for handling HTTP POST requests

Typically when developing HTTP-based Servlets, a Servlet Developer will only


concern himself with the doGet() and doPost() methods. The other methods are

© Trendz Information Technologies Ltd. Page No. 30 of 220


J2EE

considered to be methods for use by programmers very familiar with HTTP


programming.

Conditional GET Support


The HttpServlet interface defines the getLastModified() method to support
conditional GET operations. A conditional GET operation requests a resource be sent
only if it has been modified since a specified time. In appropriate situations,
implementation of this method may aid efficient utilization of network resources.

Number of Instances
The Servlet declaration, which is part of the deployment descriptor of the Web
application containing the Servlet, “Deployment Descriptor”, controls how the Servlet
container provides instances of the Servlet.

For a Servlet not hosted in a distributed environment (the default), the Servlet
container must use only one instance per Servlet declaration. However, for a Servlet
implementing the SingleThreadModel interface, the Servlet container may instantiate
multiple instances to handle a heavy request load and serialize requests to a
particular instance.

In the case where a Servlet was deployed as part of an application marked in the
deployment descriptor as distributable, a container may have only one instance per
Servlet declaration per Java Virtual Machine (JVM). However, if the Servlet in a
distributable application implements the SingleThreadModel interface, the container
may instantiate multiple instances of that Servlet in each JVM of the container.

Note About The Single Thread Model


The use of the SingleThreadModel interface guarantees that only one thread at a
time will execute in a given Servlet instance’s service method. It is important to note
that this guarantee only applies to each Servlet instance, since the container may
choose to pool such objects. Objects that are accessible to more than one Servlet
instance at a time, such as instances of HttpSession, may be available at any
particular time to multiple Servlets, including those that implement
SingleThreadModel.

It is recommended that a developer take other means to resolve those issues instead
of implementing this interface, such as avoiding the usage of an instance variable or
synchronizing the block of the code accessing those resources. The
SingleThreadModel Interface is deprecated in this version of the specification.

Servlet Life Cycle


A Servlet is managed through a well-defined life cycle that defines how it is loaded
and instantiated, is initialized, handles requests from clients, and is taken out of
service.

This life cycle is expressed in the API by the init, service, and destroy methods of the
javax.Servlet.Servlet interface that all Servlets must implement directly or indirectly
through the GenericServlet or HttpServlet abstract classes.

Loading and Instantiation

© Trendz Information Technologies Ltd. Page No. 31 of 220


J2EE

The Servlet container is responsible for loading and instantiating Servlets. The
loading and instantiation can occur when the container is started, or delayed until the
container determines the Servlet is needed to service a request.

When the Servlet engine is started, the Servlet container must locate the Servlet
class. The Servlet container loads the Servlet class using normal Java class loading
facilities. The loading may be from a local file system, a remote file system, or other
network services. After loading the Servlet class, the container instantiates it for
use.

Initialization
After the Servlet object is instantiated, the container must initialize the Servlet
before it can handle requests from clients. Initialization is provided so that a Servlet
can read persistent configuration data, initialize costly resources (such as JDBC™ API
based connections), and perform other one-time activities. The container initializes
the Servlet instance by calling the init method of the Servlet interface with a unique
(per Servlet declaration) object implementing the ServletConfig interface. This
configuration object allows the Servlet to access name-value initialization parameters
from the Web application’s configuration information. The configuration object also
gives the Servlet access to an object (implementing the ServletContext interface)
that describes the Servlet’s runtime environment. See Chapter SRV.3, “Servlet
Context” for more information about the ServletContext interface.

Request Handling
After a Servlet is properly initialized, the Servlet container may use it to handle client
requests. Request objects of type ServletRequest represent requests. The Servlet fills
out response to requests by calling methods of a provided object of type
ServletResponse. These objects are passed as parameters to the service method of
the Servlet interface.

In the case of an HTTP request, the objects provided by the container are of types
HttpServletRequest and HttpServletResponse.

Note that a Servlet instance placed into service by a Servlet container may handle no
requests during its lifetime.

Multithreading Issues
A Servlet container may send concurrent requests through the service method of the
Servlet. To handle the requests, the Servlet developer must make adequate
provisions for concurrent processing with multiple threads in the service method.

Although it is not recommended, an alternative for the Developer is to implement the


SingleThreadModel interface which requires the container to guarantee that there is
only one request thread at a time in the service method. A Servlet container may
satisfy this requirement by serializing requests on a Servlet, or by maintaining a pool
of Servlet instances. If the Servlet is part of a Web application that has been marked
as distributable, the container may maintain a pool of Servlet instances in each JVM
that the application is distributed across.

For Servlets not implementing the SingleThreadModel interface, if the service method
(or methods such as doGet() or doPost() which are dispatched to the service method
of the HttpServlet abstract class) has been defined with the synchronized keyword,
the Servlet container cannot use the instance pool approach, but must serialize

© Trendz Information Technologies Ltd. Page No. 32 of 220


J2EE

requests through it. It is strongly recommended that Developers not synchronize the
service method (or methods dispatched to it) in these circumstances because of
detrimental effects on performance.

Thread Safety
Implementations of the request and response objects are not guaranteed to be
thread safe. This means that they should only be used within the scope of the
request handling thread.

References to the request and response objects should not be given to objects
executing in other threads as the resulting behavior may be not determine. If the
thread created by the application uses the container-managed objects, such as the
request or response object, those objects must be accessed only within the Servlet’s
service life cycle and such thread itself should have a life cycle within the life cycle of
the Servlet’s service method because accessing those objects after the service
method ends may cause indeterminist problems. Be aware that the request and
response objects are not thread safe. If those objects were accessed in the multiple
threads, the access should be synchronized or be done through the wrapper to add
the thread safety, for instance, synchronizing the call of the methods to access the
request attribute, or using a local output stream for the response object within a
thread.

End of Service
The Servlet container is not required to keep a Servlet loaded for any particular
period of time. A Servlet instance may be kept active in a Servlet container for a
period of milliseconds, for the lifetime of the Servlet container (which could be a
number of days, months, or years), or any amount of time in between.

When the Servlet container determines that a Servlet should be removed from
service, it calls the destroy method of the Servlet interface to allow the Servlet to
release any resources it is using and save any persistent state. For example, the
container may do this when it wants to conserve memory resources, or when it is
being shut down.

Before the Servlet container calls the destroy method, it must allow any threads that
are currently running in the service method of the Servlet to complete execution, or
exceed a server-defined time limit.

Once the destroy method is called on a Servlet instance, the container may not route
other requests to that instance of the Servlet. If the container needs to enable the
Servlet again, it must do so with a new instance of the Servlet’s class.

After the destroy method completes, the Servlet container must release the Servlet
instance so that it is eligible for garbage collection.

Servlet Context:

ServletContext Interface
The ServletContext interface defines a Servlet’s view of the Web application within
which the Servlet is running. The Container Provider is responsible for providing an
implementation of the ServletContext interface in the Servlet container. Using the
ServletContext object, a Servlet can log events, obtain URL references to resources,
and set and store attributes that other Servlets in the context can access.

© Trendz Information Technologies Ltd. Page No. 33 of 220


J2EE

Scope of a ServletContext Interface


There is one instance object of the ServletContext interface associated with each
Web application deployed into a container. In cases where the container is distributed
over many virtual machines, a Web application will have an instance of the
ServletContext for each JVM.

Servlets in a container that were not deployed as part of Web application are
implicitly part of a “default” Web application and have a default ServletContext. In a
distributed container, the default ServletContext is non-distributable and must only
exist in one JVM.

Initialization Parameters
The following methods of the ServletContext interface allow the Servlet access to
context initialization parameters associated with a Web application as specified by
the Application Developer in the deployment descriptor:

• getInitParameter
• getInitParameterNames

Initialization parameters are used by an Application Developer to convey setup


information. Typical examples are a Webmaster’s e-mail address, or the name of a
system that holds critical data.

Context Attributes
A Servlet can bind an object attribute into the context by name. Any attribute bound
into a context is available to any other Servlet that is part of the same Web
application. The following methods of ServletContext interface allow access to this
functionality:

• setAttribute()
• getAttribute()
• getAttributeNames()
• removeAttribute()

Context Attributes in a Distributed Container


Context attributes are local to the JVM in which they were created. This prevents
ServletContext attributes from being a shared memory store in a distributed
container. When information needs to be shared between Servlets running in a
distributed environment, the information should be placed into a session stored in a
database, or set in an Enterprise JavaBeans TM component.

Resources
The ServletContext interface provides direct access only to the hierarchy of static
content documents that are part of the Web application, including HTML, GIF, and
JPEG files, via the following methods of the ServletContext interface:
• getResource()
• getResourceAsStream()

The getResource() and getResourceAsStream() methods take a String with a leading


“/” as an argument that gives the path of the resource relative to the root of the

© Trendz Information Technologies Ltd. Page No. 34 of 220


J2EE

context. This hierarchy of documents may exist in the server’s file system, in a Web
application archive file, on a remote server, or at some other location.

THE REQUEST:
The request object encapsulates all information from the client request. In the HTTP
protocol, this information is transmitted from the client to the server in the HTTP
headers and the message body of the request.

HTTP Protocol Parameters


Request parameters for the Servlet are the strings sent by the client to a Servlet
container as part of its request. When the request is an HttpServletRequest object,
and conditions set out in “When Parameters Are Available” on page 36 are met, the
container populates the parameters from the URI query string and POST-ed data.

The parameters are stored as a set of name-value pairs. Multiple parameter values
can exist for any given parameter name. The following methods of the
ServletRequest interface are available to access parameters:

• getParameter()
• getParameterNames()
• getParameterValues()
• getParameterMap()

The getParameterValues() method returns an array of String objects containing all


the parameter values associated with a parameter name. The value returned from
the getParameter() method must be the first value in the array of String objects
returned by getParameterValues(). The getParameterMap() method returns a
java.util.Map of the parameter of the request, which contains names as keys and
parameter values as map values.

Data from the querystring and the post body are aggregated into the request
parameter set. Querystring data is presented before post body data. For example, if
a request is made with a query string of a=hello and a post body of
a=goodbye&a=world, the resulting parameter set would be ordered a=(hello,
goodbye, world).

Path parameters that are part of a GET request are not exposed. They must be
parsed from the String values returned by the getRequestURI() method or the
getPathInfo() method.

Attributes
Attributes are objects associated with a request. Attributes may be set by the
container to express information that otherwise could not be expressed via the API,
or may be set by a Servlet to communicate information to another Servlet (via the
RequestDispatcher). Attributes are accessed with the following methods of the
ServletRequest interface:

• getAttribute()
• getAttributeNames()

© Trendz Information Technologies Ltd. Page No. 35 of 220


J2EE

• setAttribute()

Only one attribute value may be associated with an attribute name.

Attribute names beginning with the prefixes of “java.” and “javax.” Are reserved for
definition by this specification. Similarly, attribute names beginning with the prefixes
of “sun.”, and “com.sun.” are reserved for definition by Sun Microsystems. It is
suggested that all attributes placed in the attribute set be named in accordance with
the reverse domain name convention suggested by the Java Programming Language
Specification1 for package naming.

Headers
A Servlet can access the headers of an HTTP request through the following methods
of the HttpServletRequest interface:

• getHeader()
• getHeaders()
• getHeaderNames()

The getHeader() method returns a header given the name of the header. There can
be multiple headers with the same name, e.g. Cache-Control headers, in an HTTP
request. If there are multiple headers with the same name, the getHeader() method
returns the first header in the request. The getHeaders() method allows access to all
the header values associated with a particular header name, returning an
Enumeration of String objects.

Headers may contain String representations of int or Date data. The following
convenience methods of the HttpServletRequest interface provide access to header
data in a one of these formats:

• getIntHeader()
• getDateHeader()

If the getIntHeader() method cannot translate the header value to an int, a


NumberFormatException is thrown. If the getDateHeader() method cannot translate
the header to a Date object, an IllegalArgumentException is thrown.

The following methods exist in the HttpServletRequest interface to access this


information:

• getContextPath()
• getServletPath()
• getPathInfo()

It is important to note that, except for URL encoding differences between the request
URI and the path parts, the following equation is always true:

requestURI = contextPath + ServletPath + pathInfo

Cookies

© Trendz Information Technologies Ltd. Page No. 36 of 220


J2EE

The HttpServletRequest interface provides the getCookies() method to obtain an


array of cookies that are present in the request. These cookies are data sent from
the client to the server on every request that the client makes. Typically, the only
information that the client sends back as part of a cookie is the cookie name and the
cookie value. Other cookie attributes that can be set when the cookie is sent to the
browser, such as comments, are not typically returned.

Lifetime of the Request Object


Each request object is valid only within the scope of a Servlet’s service method, or
within the scope of a filter’s doFilter() method. Containers commonly recycle request
objects in order to avoid the performance overhead of request object creation. The
developer must be aware that maintaining references to request objects outside the
scope described above is not recommended as it may have indeterminate results.

THE RESPONSE:
The response object encapsulates all information to be returned from the server to
the client. In the HTTP protocol, this information is transmitted from the server to the
client either by HTTP headers or the message body of the request.

Buffering
A Servlet container is allowed, but not required, to buffer output going to the client
for efficiency purposes. Typically servers that do buffering make it the default, but
allow Servlets to specify buffering parameters.

The following methods in the ServletResponse interface allow a Servlet to access and
set buffering information:

• getBufferSize()
• setBufferSize()
• isCommitted()
• reset()
• resetBuffer()
• flushBuffer()

These methods are provided on the ServletResponse interface to allow buffering


operations to be performed whether the Servlet is using a ServletOutputStream or a
Writer.

The getBufferSize() method returns the size of the underlying buffer being used. If
no buffering is being used, this method must return the int value of 0 (zero).

The Servlet can request a preferred buffer size by using the setBufferSize() method.
The buffer assigned is not required to be the size requested by the Servlet, but must
be at least as large as the size requested. This allows the container to reuse a set of
fixed size buffers, providing a larger buffer than requested if appropriate. The
method must be called before any content is written using a ServletOutputStream or
Writer. If any content has been written or the response object has been committed,
this method must throw an IllegalStateException.

© Trendz Information Technologies Ltd. Page No. 37 of 220


J2EE

The isCommitted() method returns a boolean value indicating whether any response
bytes have been returned to the client. The flushBuffer() method forces content in
the buffer to be written to the client.

The reset method clears data in the buffer when the response is not committed.
Headers and status codes set by the Servlet prior to the reset call must be cleared as
well. The resetBuffer() method clears content in the buffer if the response is not
committed without clearing the headers and status code.

If the response is committed and the reset or resetBuffer() method is called, an


IllegalStateException must be thrown. The response and its associated buffer will be
unchanged.

When using a buffer, the container must immediately flush the contents of a filled
buffer to the client. If this is the first data is sent to the client, the response is
considered to be committed.

Convenience Methods
The following convenience methods exist in the HttpServletResponse interface:

• sendRedirect()
• sendError()

The sendRedirect() method will set the appropriate headers and content body to
redirect the client to a different URL. It is legal to call this method with a relative URL
path, however the underlying container must translate the relative path to a fully
qualified URL for transmission back to the client. If a partial URL is given and, for
whatever reason, cannot be converted into a valid URL, then this method must throw
an IllegalArgumentException.

The sendError() method will set the appropriate headers and content body for an
error message to return to the client. An optional String argument can be provided to
the sendError() method, which can be used in the content body of the error.

These methods will have the side effect of committing the response, if it has not
already been committed, and terminating it. The Servlet should make no further
output to the client after these methods are called. If data is written to the response
after these methods are called, the data is ignored.

If data has been written to the response buffer, but not returned to the client (i.e.
the response is not committed), the data in the response buffer must be cleared and
replaced with the data set by these methods. If the response is committed, these
methods must throw an IllegalStateException.

Closure of Response Object


When a response is closed, the container must immediately flush all remaining
content in the response buffer to the client. The following events indicate that the
Servlet has satisfied the request and that the response object is to be closed:

• The termination of the service method of the Servlet.


• The amount of content specified in the setContentLength() method of the
response has been written to the response.

© Trendz Information Technologies Ltd. Page No. 38 of 220


J2EE

• The sendError() method is called.


• The sendRedirect() method is called.

Lifetime of the Response Object


Each response object is valid only within the scope of a Servlet’s service method, or
within the scope of a filter’s doFilter() method. Containers commonly recycle
response objects in order to avoid the performance overhead of response object
creation. The developer must be aware that maintaining references to response
objects outside the scope described above may lead to non-deterministic behavior.

Filtering
Filters are Java components that allow on the fly transformations of payload and
header information in both the request into a resource and the response from a
resource.

This describes the Java Servlet classes and methods that provide a lightweight
framework for filtering active and static content. It describes how filters are
configured in a Web application, and conventions and semantics for their
implementation.

What is a filter?
A filter is a reusable piece of code that can transform the content of HTTP requests,
responses, and header information. Filters do not generally create a response or
respond to a request as Servlets do; rather they modify or adapt the requests for a
resource, and modify or adapt responses from a resource. Filters can act on dynamic
or static content. Dynamic and static content are referred to as Web resources.

Among the types of functionality available to the developer needing to use filters are
the following:

• The accessing of a resource before a request to it is invoked.


• The processing of the request for a resource before it is invoked.
• The modification of request headers and data by wrapping the request in
customized versions of the request object.
• The modification of response headers and response data by providing
customized versions of the response object.
• The interception of an invocation of a resource after its call.
• Actions on a Servlet, on groups of Servlets, or static content by zero, one, or
more filters in a specifiable order.

Implementation Filtering Components


• Authentication filters
• Logging and auditing filters
• Image conversion filters
• Data compression filters
• Encryption filters
• Tokenizing filters
• Filters that trigger resource access events

Main Concepts

© Trendz Information Technologies Ltd. Page No. 39 of 220


J2EE

The main concepts of this filtering model are described in this section. The
application developer creates a filter by implementing the javax.Servlet.Filter
interface and providing a public constructor taking no arguments. The class is
packaged in the Web Archive along with the static content and Servlets that make up
the Web application. A filter is declared using the <filter> element in the deployment
descriptor. A filter or collection of filters can be configured for invocation by defining
<filter-mapping> elements in the deployment descriptor. This is done by mapping
filters to a particular Servlet by the Servlet’s logical name, or mapping to a group of
Servlets and static content resources by mapping a filter to a URL pattern.

Filter Lifecycle
After deployment of the Web application, and before a request causes the container
to access a Web resource, the container must locate the list of filters that must be
applied to the Web resource as described below. The container must ensure that it
has instantiated a filter of the appropriate class for each filter in the list, and called
its init(FilterConfig config) method. The filter may throw an exception to indicate that
it cannot function properly. If the exception is of type UnavailableException, the
container may examine the isPermanent attribute of the exception and may choose
to retry the filter at some later time.

When the container receives an incoming request, it takes the first filter instance in
the list and calls its doFilter() method, passing in the ServletRequest and
ServletResponse, and a reference to the FilterChain object it will use. The doFilter()
method of a filter will typically be implemented following this or some subset of the
following pattern:

Step 1:The method examines the request’s headers.


Step 2:The method may wrap the request object with a customized implementation
of ServletRequest or HttpServletRequest in order to modify request
headers or data.
Step 3:The method may wrap the response object passed in to its doFilter method
with a customized implementation of ServletResponse or HttpServletResponse
to modify response headers or data.
Step 4:The filter may invoke the next entity in the filter chain. The next entity may
be another filter, or if the filter making the invocation is the last filter
configured in the deployment descriptor for this chain, the next entity is the
target Web resource. The filter chain’s implementation of the doFilter
method, provided by the container, must locate the next entity in the filter
chain and invoke its doFilter() method, passing in the appropriate request and
response objects. Alternatively, the filter chain can block the request by not
making the call to invoke the next entity, leaving the filter responsible for
filling out the response object.
Step 5:After invocation of the next filter in the chain, the filter may examine
response headers.
Step 6:Alternatively, the filter may have thrown an exception to indicate an error in
processing. If the filter throws an UnavailableException during its doFilter
processing, the container must not attempt continued processing down the
filter chain. It may choose to retry the whole chain at a later time if the
exception is not marked permanent.
Step 7:When the last filter in the chain has been invoked, the next entity accessed is
the target Servlet or resource at the end of the chain.

© Trendz Information Technologies Ltd. Page No. 40 of 220


J2EE

Step 8: Before a filter instance can be removed from service by the container, the
container must first call the destroy method on the filter to enable the filter to
release any resources and perform other cleanup operations.

Configuration of Filters in a Web Application


A filter is defined in the deployment descriptor using the <filter> element. In this
element, the programmer declares the following:

• filter-name: used to map the filter to a Servlet or URL


• filter-class: used by the container to identify the filter type
• init-params: initialization parameters for a filter

Optionally, the programmer can specify icons, a textual description, and a display
name for tool manipulation. The container must instantiate exactly one instance of
the Java class defining the filter per filter declaration in the deployment descriptor.
Hence, two instances of the same filter class will be instantiated by the container if
the developer makes two filter declarations for the same filter class. Here is an
example of a filter declaration:

<filter>
<filter-name>Image Filter</filter-name>
<filter-class>com.acme.ImageServlet</filter-class>
</filter>

Once a filter has been declared in the deployment descriptor, the assembler uses the
<filter-mapping> element to define Servlets and static resources in the Web
application to which the filter is to be applied. Filters can be associated with a Servlet
using the <Servlet-name> element. For example, the following code example maps
the Image Filter filter to the ImageServlet Servlet:

<filter-mapping>
<filter-name>Image Filter</filter-name>
<Servlet-name>ImageServlet</Servlet-name>
</filter-mapping>

Here the Logging Filter is applied to all the Servlets and static content pages in the
Web application, because every request URI matches the ‘/*’ URL pattern. When
processing a <filter-mapping> element using the <url-pattern> style, the container
must determine whether the <url-pattern> matches the request URI. The order the
container uses in building the chain of filters to be applied for a particular request
URI is as follows:

1. First, the <url-pattern> matching filter mappings in the same order that
these elements appear in the deployment descriptor.
2. Next, the <Servlet-name> matching filter mappings in the same order that
these elements appear in the deployment descriptor.

This requirement means that the container, when receiving an incoming request,
processes the request as follows:

• If there are filters matched by Servlet name and the Web resource has a
<Servlet-name>, the container builds the chain of filters matching in the

© Trendz Information Technologies Ltd. Page No. 41 of 220


J2EE

order declared in the deployment descriptor. The last filter in this chain
corresponds to the last <Servlet-name> matching filter and is the filter that
invokes the target Web resource.
• If there are filters using <url-pattern> matching and the <url-pattern>
matches the request URI. The last filter in this chain is the last <url-pattern>
matching filter in the deployment descriptor for this request URI. The last
filter in this chain is the filter that invokes the first filter in the <Servlet-
name> matching chain, or invokes the target Web resource if there are none.

It is expected that high performance Web containers will cache filter chains so that
they do not need to compute them on a per-request basis.

Sessions
The Hypertext Transfer Protocol (HTTP) is by design a stateless protocol. To build
effective Web applications, it is imperative that requests from a particular client be
associated with each other. Many strategies for session tracking have evolved over
time, but all are difficult or troublesome for the programmer to use directly. This
specification defines a simple HttpSession interface that allows a Servlet container to
use any of several approaches to track a user’s session without involving the
Application Developer in the nuances of any one approach.

Session Tracking Mechanisms


The following sections describe approaches to tracking a user’s sessions

Cookies
Session tracking through HTTP Cookies is the most used session tracking mechanism
and is required to be supported by all Servlet containers. The container sends a
cookie to the client. The client will then return the cookie on each subsequent
request to the server, unambiguously associating the request with a session. The
name of the session tracking cookie must be JSESSIONID.

URL Rewriting
URL Rewriting is the lowest common denominator of session tracking. When a client
will not accept a cookie, the server as the basis for session tracking may use URL
rewriting. URL rewriting involves adding data, a session ID, to the URL path that is
interpreted by the container to associate the request with a session. The session ID
must be encoded as a path parameter in the URL string. The name of the parameter
must be jsessionid. Here is an example of a URL containing encoded path
information:

http://www.myserver.com/catalog/index.html;jsessionid=1234

Creating a Session
A session is considered “new” when it is only a prospective session and has not been
established. Because HTTP is a request-response based protocol, an HTTP session is
considered to be new until a client “joins” it. A client joins a session when session
tracking information has been returned to the server indicating that a session has
been established. Until the client joins a session, it cannot be assumed that the next
request from the client will be recognized as part of a session.

The session is considered to be “new” if either of the following is true:

© Trendz Information Technologies Ltd. Page No. 42 of 220


J2EE

• The client does not yet know about the session


• The client chooses not to join a session.

These conditions define the situation where the Servlet container has no mechanism
by which to associate a request with a previous request.

A Servlet Developer must design his application to handle a situation where a client
has not, cannot, or will not join a session.

Session Scope
HttpSession objects must be scoped at the application (or Servlet context) level. The
underlying mechanism, such as the cookie used to establish the session, can be the
same for different contexts, but the object referenced, including the attributes in that
object, must never be shared between contexts by the container.

Session Timeouts
In the HTTP protocol, there is no explicit termination signal when a client is no longer
active. This means that the only mechanism that can be used to indicate when a
client is no longer active is a timeout period. The default timeout period for sessions
is defined by the Servlet container and can be obtained via the
getMaxInactiveInterval() method of the HttpSession interface. This timeout can be
changed by the Developer using the setMaxInactiveInterval() method of the
HttpSession interface.

The timeout periods used by these methods are defined in seconds. By definition, if
the timeout period for a session is set to -1, the session will never expire. The
session invalidation will not take effect until all Servlets using that session have
exited the service method. Once the session invalidation is initiated, a new request
must not be able to see that session.

Dispatching Requests
When building a Web application, it is often useful to forward processing of a request
to another Servlet, or to include the output of another Servlet in the response. The
RequestDispatcher interface provides a mechanism to accomplish this.

Obtaining a RequestDispatcher
An object implementing the RequestDispatcher interface may be obtained from the
ServletContext via the following methods:

• getRequestDispatcher()
• getNamedDispatcher()

The getRequestDispatcher() method takes a String argument describing a path


within the scope of the ServletContext. This path must be relative to the root of the
ServletContext and begin with a ‘/’. The method uses the path to look up a Servlet,
using the Servlet path. If no Servlet can be resolved based on the given path, a
RequestDispatcher is provided that returns the content for that path.

The behavior of this method is similar to the method of the same name in the
ServletContext. The Servlet container uses information in the request object to
transform the given relative path against the current Servlet to a complete path.

© Trendz Information Technologies Ltd. Page No. 43 of 220


J2EE

For example, in a context rooted at ’/’ and a request to /garden/tools.html, a request


dispatcher obtained via ServletRequest.getRequestDispatcher("header.html") will
behave exactly like a call to ServletContext.getRequestDispatcher("/Trendz/
header.html").

Using a Request Dispatcher


To use a request dispatcher, a Servlet calls either the include method or forward
method of the RequestDispatcher interface. The parameters to these methods can be
either the request and response arguments that were passed in via the service
method of the javax.Servlet interface, or instances of subclasses of the request and
response wrapper classes that were introduced for version 2.3 of the specification. In
the latter case, the wrapper instances must wrap the request or response objects
that the container passed into the service method.

The Container Provider should ensure that the dispatch of the request to a target
Servlet occurs in the same thread of the same JVM as the original request.

The include() Method


The include() method of the RequestDispatcher interface may be called at any time.
The target Servlet of the include method has access to all aspects of the request
object, but its use of the response object is more limited.

It can only write information to the ServletOutputStream or Writer of the response


object and commit a response by writing content past the end of the response buffer,
or by explicitly calling the flushBuffer() method of the ServletResponse interface. It
cannot set headers or call any method that affects the headers of the response. Any
attempt to do so must be ignored.

Included Request Parameters


Except for Servlets obtained by using the getNamedDispatcher() method, a Servlet
that has been invoked by another Servlet using the include method of
RequestDispatcher has access to the path by which it was invoked.

The Forward Method


The forward method of the RequestDispatcher interface may be called by the calling
Servlet only when no output has been committed to the client. If output data exists
in the response buffer that has not been committed, the content must be cleared
before the target Servlet’s service method is called. If the response has been
committed, an IllegalStateException must be thrown.

The path elements of the request object exposed to the target Servlet must reflect
the path used to obtain the RequestDispatcher.

The only exception to this is if the RequestDispatcher was obtained via the
getNamedDispatcher() method. In this case, the path elements of the request object
must reflect those of the original request.

Before the forward method of the RequestDispatcher interface returns, the response
content must be sent and committed, and closed by the Servlet container.

© Trendz Information Technologies Ltd. Page No. 44 of 220


J2EE

© Trendz Information Technologies Ltd. Page No. 45 of 220


J2EE

JAVA RMI OVERVIEW

Distributed systems required that computations running in different address spaces,


potentially on different hosts, be able to communicate. For a basic communication
mechanism, the Java Language supports sockets, which are flexible and sufficient
for general communication. However, sockets require the client and decode
messages for exchange, and the design of such protocols is cumbersome and can be
error-prone.

An alternative to sockets is Remote Procedure Call (RPC), which abstracts the


communication interface to the level of a procedure call. Instead of working directly
with sockets, the programmer has the illusion of calling a local procedure, when in
fact the arguments of the call are packaged up and shipped off to the remote target
of the call. RPC systems encode arguments and return values using an external data
representation, such as XDR.

RPC, however, does not translate well into distributed object system, where
communication between program-level objects residing in different address spaces is
needed. In order to match the semantics of object invocation, distributed object
systems require Remote Method Invocation or RMI. In such systems, a local
surrogate (stub) object manages the invocation on a remote object.

The Java remote method invocation system described in this specification has been
specifically designed to operate in the Java environment. The Java language’s RMI
system assumes the homogeneous environment of the Java virtual Machine, and the
system can therefore take advantage of the Java object model whenever possible.

System Goals
The goals for supporting distributed objects in the Java language are:

 Support seamless remote invocation on objects in different virtual machines.


 Support callbacks from servers to applets.
 Integrate the distributed object model into the Java language in a natural way
while retaining most of the Java language’s object semantics.
 Make differences between the distributed object model and local Java object
model apparent.
 Make writing reliable distributed applications as simple as possible.
 Preserver the type-safety provided by the Java runtime environment.
 Various reference semantics for remote objects; for example live (Non-
Persistent) references, persistent references, and lazy activation.
 The safe Java environment provided by security managers and class loaders.

Underlying all these goals is a general requirement that the RMI model be both
simple (easy to use) and natural (fits well in the language).

The first two chapters in this specification describe the distributed object model for
the Java language and the system overview. The remaining chapters describe the
RMI client and server visible APIs which are part of JDK 1.2.

Distributed Object Applications


RMI applications are often comprised of two separate programs i.e. a server and a
client. A typical server application creates a number of remote objects, makes
references to those remote objects accessible, and waits for clients to invoke

© Trendz Information Technologies Ltd. Page No. 46 of 220


J2EE

methods on those remote objects. A typical client application gets a remote


reference to one or more remote objects in the server and then invokes methods on
them. RMI provides the mechanism by which the server and the client communicate
and pass information back and forth. Such an application is sometimes referred to as
a Distributed Object Application.

Distributed Object Applications need to:

 Locate Remote Objects:


Applications can use one of two mechanisms to obtain references to remote
objects. An application can register its remote objects with RMI’s simple
naming facility, the rmiregistry, or the application can pass and return remote
object references as part of its normal operation.

 Communicate with Remote Objects:


Details of communication between remote objects are handled by RMI; to the
programmer, remote communication looks like a standard Java Method
Invocation.

 Load class bytecodes for objects that are passed as parameters or return
values because RMI allows a caller to pass pure Java objects to remote
objects. RMI provides the necessary mechanisms for loading an object’s code
as well as transmitting its data.

The illustration below depicts an RMI distributed application that uses the registry to
obtain references to a remote object. The server calls the registry to associate a
name with a remote object. The client looks up the remote object by its name in the
server’s registry and then invokes a method on it. The illustration also shows that the
RMI system uses an existing web server to load Java class bytecodes from server to
client and from client to server, for objects when needed. RMI can load class
bytecodes using any URL protocol (Ex. HTTP, FTP, File etc.) that is supported by the
Java System.

Definition of Terms
In the Java distributed object model, a remote object is one whose methods can be
invoked from another Java Virtual Machine, potentially on a different host. An object
of this type is described by one or more remote interfaces, which are Java interfaces
that declare the methods of the remote objects.

Remote Method Invocation (RMI) is the action of invoking a method of a remote


interface on a remote object. Most importantly, a method invocation on a remote
object has the same syntax as a method invocation on a local object.

The Distributed and Non Distributed Models Contrasted


The Java distributed object model is similar to the Java object model in the following
ways:

 A reference to a remote object can be passed as an argument or returned as


a result in any method invocation (Local or Remote).
 A remote object can be cast to any of the set of remote interfaces supported
by the implementation using the built in Java syntax for casting.
 The built in Java instance of operation can be used to test the remote
interfaces supported by a remote object.

© Trendz Information Technologies Ltd. Page No. 47 of 220


J2EE

The Java distributed object model differs from the Java object model in these ways:

 Clients of remote objects interact with remote interfaces, never with the
implementation classes of those interfaces.
 Non-Remote arguments to, and results from, a remote method invocation are
passed by copy rather than by references to objects are only useful within a
single virtual machine.
 A remote object is passed by references, not by copying the actual remote
implementation.
 The semantics of some of the methods defined by class java.lang.object are
specialized for remote objects.
 Since the failure modes of invoking remote objects are inherently more
complicated than the failure modes of invoking local objects, clients must deal
with additional exceptions that can occur during a remote method invocation.

Overview of RMI Interfaces and Classes


The interfaces and classes that are responsible for specifying the remote behavior of
the RMI system are defined in the java.rmi package hierarchy. The following figure
shows the relationship between several of these interfaces and classes:

The java.rmi Remote Interface


In RMI, a remote interface is an interface that declares a set of methods that may be
invoked from a remote Java Virtual Machine. A remote interface must satisfy the
following requirements.

 A remote interface must at least extend, either directly or indirectly, the


interface java.rmi.remote.
 Each method declaration in a remote interface must satisfy the requirements
of a remote method declaration as follows:
 A remote method declaration must include the exception
java.rmi.RemoteException (or one of its superclasses such as
java.io.IOException or java.lang.Exception) in its throws clause. In
addition to any application specific exceptions (note that application specific
exceptions do not have to extend java.rmi.RemoteException).
 In a remote method declaration, a remote object declared as a parameter or
return value (either declared directly in the parameter list or embedded within
a non-remote object in a parameter) must be declared as the remote
interface, not the implementation class of that interface.

The interface java.rmi.Remote is a marker interface that defines no methods:

public interface remote {}

Implementing a Remote Interface


The general rules for a class that implements a remote interface are as follows:

 That class usually e3xtends java.rmi.server.UnicastRemoteObject,


thereby inheriting the remote behavior provided by the classes
java.rmi.server.RemoteObject and java.rmi.server.RemoteServer.
 The class can implement any number of remote interface.
 The class can extend another remote implementation class.

© Trendz Information Technologies Ltd. Page No. 48 of 220


J2EE

 The class can define methods that do not appear in the remote interface, but
those methods can only be used locally and are not available remotely.

Parameter Passing in Remote Method Invocation


An argument to, or a return value from, a remote object can be any Java object that
is serializable. This includes Java primitive types, remote Java objects, and non-
remote Java Objects that implement the java.io.Serializable interface. For more
details on how to make classes serializable, see the Java “Object Serialization
Specification”. Classes, for parameters or return values that are not available locally
are downloaded dynamically by the RMI system.

Passing Non-Remote Objects


A Non-Remote Object, that is passed as a parameter of a remote method invocation
or returned as a result of a remote method invocation, is passed by copy; that is, the
object is serialized using the Java Object Serialization Mechanism.

So, when a Non-Remote Object is passed as an argument or return value in a remote


method invocation, the content of the Non-Remote Object is copied before invoking
the call on the remote object.

When a Non-Remote Object is returned from a remote method invocation, a new


object is created in the calling virtual machine.

Passing Remote Objects


When passing a remote object as a parameter or return value in a remote method
call, the stub for the remote object is passed. A remote object passed as a
parameter can only implement remote interfaces.

Referential Integrity
If two references to an object are passed from one VM to another VM in parameters
(or in the return value) in a single remote method call and those references refer to
the same object in the sending VM, those references will refer to a single copy of the
object in the receiving VM. More generally stated: within a single remote method
call, the RMI system maintains referential integrity among the objects passed as
parameters or as a return value in the call.

Class Annotation
When an object is sent from one VM to another in a remote method call, the RMI
system annotates the class descriptor in the call stream with information (the URL)
of the class so that the class can be loaded at the receiver. It is a requirement that
classes be downloaded on demand during remote method invocation.

Parameter Transmission
Parameters in an RMI call are written to a stream that is a subclass of the class
java.io.ObjectOutputStream in order to serialize the parameters to the destination
of the remote call. The ObjectOutputStream subclass overrides the replace Object
method to replace each remote object with its corresponding stub class. Parameters
that are objects are written to the stream using the ObjectOutputStream’s
writeObject() method.

© Trendz Information Technologies Ltd. Page No. 49 of 220


J2EE

ObjectOutputStream calls the replaceObject() method for each object written to


the stream via the writeObject() method (that includes objects referenced by those
objects that are written). The replaceObject() method of RMI’s subclass of
ObjectOutputStream returns the following.

 If the object passed to replaceObject is an instance of java.rmi.Remote, then


it returns the stub for the remote object. A stub for a remote object is
obtained via a call to the method java.rmi.server.RemoteObject.toStub.
 If the object passed to replaceObject is not an instance of java.rmi.Remote
then the object is simply returned.

RMI’s subclass of ObjectOutputStream also implements the annotateClass method


that annotates the call stream with the location of the class so that it can be
downloaded at the receiver.

Since parameters are written to a single ObjectOutputStream, references that


refer to the same object at the caller will refer to the same copy of the object at the
receiver. At the receiver, parameters are read by a single ObjectInputStream.

Any other default behavior of ObjectOutputStream for writing objects (and


similarly ObjectInputStream for reading objects) is maintained in parameter
passing. For example, the calling of writeReplace when writing objects and
readResolve when reading objects is honored by RMI’s parameter marshal and un-
marshal streams.

In a similar manner to parameter passing in RMI as described above, a return value


(or exception) is written to a subclass of ObjectOutputStream and has the same
replacement behavior as parameter transmission.

Locating Remote Objects


A simple bootstrap name server is provided for storing named references to remote
objects. A remote object reference can be stored using the URL-based methods of
the class java.rmi.Naming.

For a client to invoke a method on a remote object, that client must first obtain a
reference to the object. A reference to a remote object is usually obtained as a
parameter or return value in a method call. The RMI system provides a simple
bootstrap name server from which to obtain remote objects on given hosts. The
java.rmi.Naming class provides Uniform Resource Locator (URL) based methods to
look up, bind, rebind, unbind, and list the name-object pairings maintained on a
particular host and port.

Stubs and Skeletons


RMI uses a standard mechanism (employed in RPC systems) for communicating with
remote objects: Stubs and Skeletons. A stub for a remote object acts as a client’s
local representative or proxy for the remote object. The caller invokes a method on
the local stub, which is responsible for carrying out the method call on the remote
object. In RMI, a stub for remote object implements the same set of remote
interfaces that a remote object implements.

© Trendz Information Technologies Ltd. Page No. 50 of 220


J2EE

When a stub’s method is invoked, it does the following:

 Initiates a connection with the remote VM containing the remote object.


 Marshals (writes and transmits) the parameters to the remote VM.
 Waits for the result of the method invocation.
 Unmarshals (reads) the return value or exception returned
 Returns the value to the caller.

The stub hides the serialization of parameters and the network-level communication
in order to present a simple invocation mechanism to the caller.

In the remote VM, each remote object may have a corresponding skeleton (in JDK
1.2 only environments, skeletons are not required). The skeleton is responsible for
dispatching the call to the actual remote object implementation. When a skeleton
receives an incoming method invocation it does the following;

 Unmarshals (reads) the parameters for the remote method.


 Invokes the method on the actual remote object implementation.
 Marshals (writes & transmits) the results (return value or exception) to the
caller.

In JDK 1.2 and additional stub protocol was introduced that eliminates the need for
skeletons in JDK 1.2 only environments. Instead, generic code is used to carry out
the duties performed by skeletons in JDK 1.1. The rmic compiler generates stubs and
skeletons.

Threads Usage in Remote Method Invocation


A method dispatched by the RMI runtime to a remote object implementation may or
may not execute in a separate thread. The RMI runtime makes no guarantees with
respect to mapping remote object invocations to threads. Since remote method
invocation on the same remote object may execute concurrently, a remote object
implementation needs to make sure its implementation is thread-safe.

Garbage Collection of Remote Objects


In a distributed system, just as in the local system, it is desirable to automatically
delete those remote objects that are no longer referenced by any client. This fees the
programmer from needing to keep track of the remote objects clients so that it can
terminate approximately. RMI uses a reference-counting garbage collection algorithm
similar to Modula-3’s Network Objects.

To accomplish reference-counting garbage collection, the RMI runtime keeps track of


all live references within each Java Virtual Machine. When a live reference enters a
Java Virtual Machine, its reference count is incremented. The first reference to an
object sends a “Referenced” message to the server for the object. As live references
are found to be unreferenced in the local virtual machine, the count is decremented.
When the last reference has been discarded, an unreferenced message is sent to the
server. Many subtleties exist in the protocol; most of these are related to maintaining
the ordering of referenced and unreferenced messages in order to ensure that the
object is not prematurely collected.

When any client does not references a remote object, the RMI runtime refers to it
using a weak reference. The weak reference allows the Java Virtual Machine’s
garbage collector to discard the object if not other local references to the object

© Trendz Information Technologies Ltd. Page No. 51 of 220


J2EE

exist, the distributed garbage collection algorithm interacts with the local Java Virtual
Machine’s garbage collector in the usual ways by holding normal or weak references
to objects.

As long as a local reference to a remote object exists, it cannot be garbage-collected


and it can be passed in remote calls or returned to clients. Passing a remote object
adds the identifier for the virtual machine to which it was passed to the referenced
set. A remote object needing unreferenced notification must implement the
java.rmi.server. Unreferenced interface. When those references no longer exist, the
unreferenced method will be invoked. Unreferenced is called when the set of
references is found to be empty so it might be called more than once. Remote
objects are only collected when no more references either local or remote, still exist.

Note that if a network partition exists between a client and a remote server object, it
is possible that premature collection of the remote object will occur (since the
transport might believe that the client crashed). Because of the possibility of
premature collection, remote references cannot guarantee referential integrity; in
other words, it is always possible that a remote reference may in fact not refer to an
existing object. An attempt to use such a reference may in fact not refer to an
existing object. An attempt to use such a reference will generate a
RemoteException, which must be handled by the application.

Understating the first RMI application.

The Remote Interface

import java.rmi.*;
public interface Rmilnter extends Remote
{
public double getSqrt(doubled) thros RemoteException;
}

The ServerImplementation Class

import java.rmi.*;
import java.rmi.server.*;
public class ServerImpl extends UnicastRemoteObject implements RmiInter
{
public ServerImpl() throws RemoteException
{
System.Out.println(“Object created”)
}
public double getSqrt(double d) throws RemoteException
{
return Math.sqrt(d);
}
public static void main (String args[]) throws Exception
{
ServerImpl si=new ServerImpl();
Naming.rebind(“server”,si);
System.out.println(“Object bounded to remote network”);
}

© Trendz Information Technologies Ltd. Page No. 52 of 220


J2EE

Client Application

import java.io.*;
import java.rmi.*;
public class Client
{
public satic void main(String args[]) throws Exception
{
RmiInter ri=(RmiInter) Naming.lookup(“rmi://localhost:1099/server”);
DataInputStream dis=new DataInputStream(System.in);
System.out.println(“Enter a double number to know its sqrt”);
String num=dis.readLine();
double d=Double.parseDouble(num);
System.out.prinIn(“The Sqr is” +ri.getDouble(d));
}
}

Executing the application

a. Compile the 3 applications with “javac”.


b. Generate Stub and Skeleton for the server implementation class.
c. Start the rmiregistry. On prompt c:\> rmiregistry.
d. Take 2nd prompt, start the rmiserver.
e. Take 3rd prompt, start the rmiclient.

Remote Callbacks
We have seen earlier how a client can get a reference to a remote object as result of
a method invocation. A client also can be a remote object. In some situations, a
server may need to make a remote call to a client.

Example for Understanding the Remote Callbacks


When a Client application is observed, it is first looking the server object and calling
the methods using the remote reference. In remote callback server need not to
lookup the client object explicitly because when client requests the connection, the
server identifies the client, using which it can call a method on the client.

For Callback an interface for client is also required

CallClientInter.java
import java.rmi.*;
public interface CallClientInter extends remote
{
public void msgPopup(String msg) throws RemoteException;
}

CallServerInter.java
import java.rmi.*;
public interface CallServerInter extends Remote
{
public String sayHello(CallClientInter cci) throws RemoteException;
}

© Trendz Information Technologies Ltd. Page No. 53 of 220


J2EE

CallClientImpl.java
import java.applet.*;
import java.awt.*;
import java.io.Serializable;
import java.rmi.*
import java.rmi.server.*;
public class CallClientImpl extends Applet implements
CallClientInter.Serializable
{
Strint msg=” “;
Frame f=new Frame();
Label l1=new Label(“
Public void init()
{
f.add(l);
try
{
UnicastRemoteObject.exportObject(this);
String host=”rmi://”+getcodeBase().getHost()+”HelloServer”;
CallServerInter csi=(CallServerInter) Naming.lookup(host);
Msg=csi.getSayHello(CallClientInter) this);
}
}
Public void paint(Graphics g)
{
g.drawstring(msg,50,50);
}
public void msgPopup(String s) throws RemoteException
{
l1.setText(s);
f.setSize(100,100);
f.setVisible(true);
}
}

CallClientImpl.html

<applet code=CallClientImpl width=300 height=300>


</applet>

CallServerImpl.java

import java.util.Date;
import java.rmi.*;
import java.rmi.server.*;
public class CallServerImpl extends UnicastRemoteObject implements
CallServerInter
{
public CallServerImpl() throws RemoteException
{
System.out.println(“Object created”);
}

© Trendz Information Technologies Ltd. Page No. 54 of 220


J2EE

public String say Hello(CallClientInter cci) throws Remote Exception


{
cci.msgPopip(“Hello From Server”);
return “Current Date” +new Date();
}
public static void main(String args[]) throws Exception
{
CallServerImpl csi=new CallServerImpl();
Naming.rebind(“HelloServer”,csi);
}
}

Dynamic Class Loading


RMI allows parameters; return values and exceptions passed in RMI calls to be any
object that is serializable. RMI uses the object serialization mechanism to transmit
data from one virtual machine to another and also annotates the call stream with the
appropriate location information so that the class definition files can be loaded at the
receiver.

When parameters and return values for a remote method invocation are
unmarshalled to become live objects in the receiving VM, class definitions are
required for all of the types of objects in the stream. The unmarshalling process first
attempts to resolve classes by name in its local class loading context (the context
class loader of the current thread). RMI also provides a facility for dynamically
loading the class definitions for the actual types of objects passed as parameters and
return values for remote method invocations from network locations specified by the
transmitting endpoint. This includes the dynamic downloading of remote stub classes
corresponding to particular remote object implementation classes (and used to
contain remote references) as well as any other type that is passed by value in RMI
calls, such as the subclass of a declared parameter type, that is not already available
in the class loading context of the unmarshalling side.

To support dynamic class loading, the RMI runtime uses special subclasses of
java.io.ObjectOutputStream and java.io.ObjectInputStream for the marshal
streams that it uses for marshalling and unmarshalling RMI parameters and return
values. These subclasses override the annotateClass method of
ObjectOutputStream and the resolveClass method of ObjectInputStream to
communicate information about where to locate class files containing the definitions
for classes corresponding to the class descriptors in the stream.

For every class descriptor written to an RMI marshal stream, the annotateClass
method adds to the stream the result of calling java.rmi.server.RMIClassLoader.
getClassAnnotation for the class object, which may be null or may be a String object
representing the codebase URL path (a space-separated list of URLs) from which the
remote endpoint should download the class definition file for the given class.

For every class descriptor read from an RMI marshal stream, the resolveClass
method reads a single object from the stream. If the object is a String (and the value
of the java.rmi.server.useCodebaseOnly, property is not “true”), then
resolveClass returns the result of calling RMIClassLoader.loadClass with the
annotated String object as the first parameter and the name of the desired class in
the class descriptor as the second parameter. Otherwise, resolveClass returns the

© Trendz Information Technologies Ltd. Page No. 55 of 220


J2EE

result of calling RMIClassLoader.loadClass with the name of the desired class as the
only parameter.

When a client requests a reference to the remote object, the registry returns the
stub to the client. The client looks for the class definition of the stub in its local
classpath (by default) and if found, the client loads the class or utilizes its codebase
property.

Based upon the above, five potential configuration can be set up to distribute
classes.

a. Closed
There is no dynamic loading of classes. JVM loads the classes from local
classpath only.

b. Dynamic Client Side


On the client, some classes loaded from the local classpath and some from
the codebase specified by the server.

c. Dynamic Server Side


Similar to dynamic lcient. Some classes loaded from the classpath and some
from codebase specified by the client.

d. BootStrapped Client
On the client, all the classes are loaded from codebase specified by the server.

e. BootStrapped Server
On the server, all the classes are loaded from codebase specified by the client.

Example:

BootInter.java

import java.rmi.*;
public interface BootInter extends Remote
{
public String sayHello() throws RemoteException;
}

BootInterImpl.java

import java.rmi.*;
import java.rmi.server.*;
public class bootInterImpl extends UnicastRemoteObject implements BootInter
{
public BootInterImpl() throws RemoteException
{
System.out.println(“Remote object created”);
}
public String sayHello() throws RemoteException
{
return “Hellow from Server”;
}

© Trendz Information Technologies Ltd. Page No. 56 of 220


J2EE

BootServer.java

import java.rmi.*;
import java.util.*;
public class BootServer
{
public static void main (String args[]) throws Exception
{
Properties p=System.getProperties();
String s=p.getProperty(“java.rmi.server.codebase”);
Class c=RMIClassLoader.loadClass(s,”BootInterImpl”);
Naming.rebind(“bootserver”,(Remote) c.newInstance());
System.out.println(“Object Created”);
}
}

Client.java

Import java.rmi.*;
Public class Client
{
public Client()
{
try
{
BootInter bi=(BootInter) Naming.lookup(“rmi://localhost:1099/bootserver”);
System.out.println(bi.sayHello());
}catch(Exception e){}
}
}

BootClient.java

import java.rmi.*;
import java.rmi.server.*;
import java.util.*;
public class BootClient
{
public static void main(String args[]) throws Exception
{
Properties p=System.getProperties();
String s=p.getProperty(“java.rmi.server.codebase”);
Class c=RMIClassLoader.loadClass(S,”Client”);
c.newInstance();
}
}

Object Activation

© Trendz Information Technologies Ltd. Page No. 57 of 220


J2EE

The remote objects discussed so far are instances of java.rmi.UnicastRemoteObject


class, and they are accessible all time, even when there are no clients executing.
Consider, when the number remote objects and resources used by them, on a server
are high.

To overcome this, Object Activation was introduced.

Object Activation
Allows remote objects to be executed on as needed basis i.e. when an ‘activatable’
remote object is accessed (via a method invocation) if that remote object is not
currently executing the system initiates the objects execution inside an appropriate
JVM. RMI uses lazy activation, this is where the activation of an object is deferred
until a client first use, the first method invocation.

To understand the actual semantics of using activation model, let us understand few
terms.

Activator
It facilitates remote object activation by keeping track of all the information needed
to activate an object and is responsible for starting the instances of JVMs on the
server.

Activation Group
Activation Group creates instances of objects in its group, and informs its monitor
about the various active and passive states.

Activation Monitor
Every Activation Group have an Activation Monitor that keeps track of an object’s
state in the group and the group’s state as a whole.

Activation System
The Activation System provides a means for registering groups and activatable
objects to be activated within those groups.

© Trendz Information Technologies Ltd. Page No. 58 of 220


J2EE

Overview & Importance

Web Application Development

JSP Directives

JSP Action Tags

JSP Custom Tag Library

© Trendz Information Technologies Ltd. Page No. 59 of 220


J2EE

JAVA SERVER PAGES OVERVIEW

The Internet was once full of Web sites hosting static pages or simple forms at best.
Now it's an interactive environment for transacting daily business, from shopping to
trading stocks to interacting with suppliers, in a personalized and dynamic setting.
Today, the tools and products to build dynamic, Web-based applications are still
maturing. Traditionally, companies used CGI applications to generate dynamic
content for Web pages. But that solution hasn't scaled well to support complex
functionality and growing numbers of concurrent users. Java Server Pages
technology provides a highly scalable method for creating dynamic content for the
Web. As part of the Java family of APIs, JSP technology shares the “Write Once, Run
Anywhere” benefits of the Java platform, with easy access to a broad range of Java
APIs. JSP technology enables a tiered development methodology that lets
organizations leverage internal programming expertise to create applications that are
fast to deploy and easy to maintain.

Multi-tier Application Architectures


The growth and acceptance of the Internet in both businesses and homes is changing
the face of many industries - and the information systems that support them. From
new .com companies to brick-and-mortar establishments, businesses everywhere are
finding new ways to leverage the power of the Internet. Software developers have
been quick to realize the possibilities of Web-based clients in application
architectures. With a browser on virtually every desktop, companies can deploy a
multi-tier architecture in which Web servers act as a middle tier, managing
interactions with Web-based clients.

The Web-based client architecture may have three or more layers. This multi-tier
architecture provides many benefits over a traditional (two-tiered) client/server
architecture.

Development Tools and Methodologies Are Maturing


Companies building and deploying applications on this model are faced with an
application environment that is still maturing. A number of different technologies -
ranging from traditional CGI scripts to JSP technology - are available today to build
the interactive, "customer-facing" component of these applications. The challenge is
selecting an application architecture and component design that meets the evolving
user needs (whether they be customers, partners, or internal staff) as well as the
enterprise's own IT requirements.

Users Have Heightened Requirements


Internet users have heightened expectations for application availability and reliability.
They want to be able to access applications at any time of day or night to perform a
wide variety of tasks online. They expect up-to-date information and fast response
times.

To support these requirements, application providers need high-performance, highly


reliable applications that can be updated easily. They need applications that can scale
to support large numbers of users, and that can interact with vital business systems.

Solutions must meet demanding Enterprise Software Requirements

© Trendz Information Technologies Ltd. Page No. 60 of 220


J2EE

The organizations that are building and maintaining these applications also have
stringent requirements when selecting the architectures, products, and tools for
creating Web-based applications.

 The development platform must support fast application deployment and


rapid updates.
 The application must be easy to maintain using minimal developer resources.
Many organizations face a shortage of qualified Web developers and need to
protect the developers they already have.
 Finally, the organization needs to retain the ability to adopt new tools or
technologies as needed, so the development environment should not close out
options. With new tools, systems, and information sources appearing nearly
every day, there is a risk to selecting a solution that leaves the organization
entirely at the mercy of a single vendor - even if that vendor is the market
leader.

JSP Technology: The Next Evolution of Servlets


JSP technology is a means for creating dynamic Web-based content using server-side
(middle-tier) processing. JSP simplifies the process of creating these dynamic pages
by separating the application logic from the page design and encapsulating logic in
portable, reusable Java components.

JSP technology has evolved from the powerful servlet technology. (Servlets are Java
technology-based, server-side applications.) JSP extends the servlet technology in
many ways, making it easier and faster to build, deploy, and maintain server-side
applications that communicate with Web-based clients. The following sections
describe where JSP technology fits in the Java family of products, how JSP can
simplify the creation and maintenance of dynamic pages, and how these pages fit
into more complex, multi-tier applications.

JSP Extends Servlets


In a sense, JSP technology does not provide new core technologies - everything that
can be done with a JSP page, can also be done by writing a servlet. Servlets have
access to the same set of Java APIs as JSP. Pages created with JSP technology are, in
fact, compiled into Servlets, so they cannot be capable of anything inherently
different.

What JSP pages do, however, is enable a different, more efficient development
methodology and simplify ongoing maintenance. This is because JSP technology truly
separates the page design and static content from the logic used to generate the
dynamic content.

The Competitive Landscape


JSP technology is not the first approach for enabling interactive Web pages -rather it
is the next step in the progression of products supporting Web-based clients.

Some of the earlier methods include CGI programs, the mod_perl plug-in for the
Apache Web Server, and Microsoft Active Server Pages (ASP). The JSP technology
surpasses these previous methods in two fundamental areas
 Portability - Pages built with JSP technology are portable across platforms and
servers, and work with portable, reusable components.
 Easier Maintenance and Development - Because the page design is truly
separate from the application logic, JSP enables tiered development and

© Trendz Information Technologies Ltd. Page No. 61 of 220


J2EE

maintenance tasks, so page authors and developers can focus on specific


areas of interest without requiring the others' help.

Some of the differences are summarized in the table below.

CGI/Perl Mod_Perl ASP JSP


Any Web server,
Microsoft IIS
Any Web Apache Web including Apache,
Web server or Personal
server Server Netscape, IIS
Web Server
today
Portable across
No No No Yes
platforms/servers
Reusable, modular code No No No Yes
VBScript,
Scripting language C, Perl Perl Java
Jscript
Memory leak protection Yes No No Yes
Supports concurrent
access without separate No Yes Yes Yes
processes

The Java Server Pages (JSP) technology provides a simplified, fast way to create web
pages that display dynamically generated content. JSP technology was designed to
make it easier and faster to build web-based applications that work with a wide
variety of web servers, application servers, browsers and development tools.

The following lines provides an overview of the JSP technology, describing the
background in which it was developed and the overall goals for the technology. It
also describes the key components of a Java TM technology-based page, in the context
of a simple example.

Developing Web-based Applications: A Background.


In its short history, the Worldwide Web has evolved from a network of basically static
information displays to a mechanism for trading stocks and buying books. There
seems to be almost no limit to the possible uses for web-based clients in diverse
applications.

Applications that can make use of browser-based clients have several advantages
over traditional client/server based applications. These include nearly unlimited client
access and greatly simplified application deployment and management. (To update
an application, a developer only needs to change one server-based program, not
thousands of client-installed applications.) As a result, the software industry is
moving quickly toward building multi-tiered applications using browser-based clients.

These increasingly sophisticated web-based applications require changes in


development technology. Static HTML is fine for displaying relatively static content;
the challenge has been creating interactive web-based applications, in which the
content of the page is based on a user request or system status, not pre-defined
text.

© Trendz Information Technologies Ltd. Page No. 62 of 220


J2EE

An early solution to this problem was the CGI-BIN interface; developers wrote
individual programs to this interface, and web-based applications called the
programs through the web server. This solution has significant scalability problems --
each new CGI request launches a new process on the server. If multiple users access
the program concurrently, these processes consume all of the web server's available
resources and the performance slows to a grind.

Individual web server vendors have tried to simplify web application development
providing "plug-ins" and APIs for their servers. These solutions are web-server
specific, and don't address the problem across multiple vendor solutions. For
example, Microsoft's Active Server PagesTM (ASP) technology makes it easier to
create dynamic content on a web page, but only works with Microsoft IIS or Personal
Web Server.

Other solutions exist, but they are not necessarily easy for the average page
designer to deploy. Technologies such as Java Servlets, for example, make it easier
to write server-based code using the Java programming language for interactive
applications. A Java Servlet is a Java technology-based program that runs on the
server (as opposed to an applet, which runs on the browser). Developers can write
Servlets that take an HTTP request from the web browser, generate the response
dynamically (possibly querying databases to fulfill the request) and then send a
response containing an HTML or XML document to the browser.

Using this approach, the entire page must be composed in the Java Servlet. If a
developer or web master wanted to tune the appearance of the page, they would
have to edit and recompile the Java Servlet, even if the logic were already working.
With this approach, generating pages with dynamic content still requires application
development expertise.

What is needed, clearly, is an industry-wide solution for creating pages with


dynamically generated content. This solution should address the limitations of
current alternatives by:

 Working on any web or application server


 Separating the application logic from the appearance of the page
 Allowing fast development and testing

Simplifying the process of developing interactive web-based applications


The Java Server Pages (JSP) technology provides a simplified, fast way to create web
pages that display dynamically generated content. JSP technology was designed to
make it easier and faster to build web-based applications that work with a wide
variety of web servers, application servers, browsers and development tools.

The following lines provides an overview of the JSP technology, describing the
background in which it was developed and the overall goals for the technology. It
also describes the key components of a Java TM technology-based page, in the context
of a simple example.

Developing Web-based Applications: A Background


In its short history, the Worldwide Web has evolved from a network of basically static
information displays to a mechanism for trading stocks and buying books. There

© Trendz Information Technologies Ltd. Page No. 63 of 220


J2EE

seems to be almost no limit to the possible uses for web-based clients in diverse
applications.

Applications that can make use of browser-based clients have several advantages
over traditional client/server based applications. These include nearly unlimited client
access and greatly simplified application deployment and management. (To update
an application, a developer only needs to change one server-based program, not
thousands of client-installed applications.) As a result, the software industry is
moving quickly toward building multi-tiered applications using browser-based clients.

These increasingly sophisticated web-based applications require changes in


development technology. Static HTML is fine for displaying relatively static content;
the challenge has been creating interactive web-based applications, in which the
content of the page is based on a user request or system status, not pre-defined
text.

An early solution to this problem was the CGI-BIN interface; developers wrote
individual programs to this interface, and web-based applications called the
programs through the web server. This solution has significant scalability problems --
each new CGI request launches a new process on the server. If multiple users access
the program concurrently, these processes consume all of the web server's available
resources and the performance slows to a grind.

Individual web server vendors have tried to simplify web application development
providing "plug-ins" and APIs for their servers. These solutions are web-server
specific, and don't address the problem across multiple vendor solutions. For
example, Microsoft's Active Server PagesTM (ASP) technology makes it easier to
create dynamic content on a web page, but only works with Microsoft IIS or Personal
Web Server.

Other solutions exist, but they are not necessarily easy for the average page
designer to deploy. Technologies such as Java Servlets, for example, make it easier
to write server-based code using the Java programming language for interactive
applications. A Java Servlet is a Java technology-based program that runs on the
server (as opposed to an applet, which runs on the browser). Developers can write
Servlets that take an HTTP request from the web browser, generate the response
dynamically (possibly querying databases to fulfill the request) and then send a
response containing an HTML or XML document to the browser.

Using this approach, the entire page must be composed in the Java Servlet. If a
developer or web master wanted to tune the appearance of the page, they would
have to edit and recompile the Java Servlet, even if the logic were already working.
With this approach, generating pages with dynamic content still requires application
development expertise.

What is needed, clearly, is an industry-wide solution for creating pages with


dynamically generated content. This solution should address the limitations of
current alternatives by:

 Working on any web or application server


 Separating the application logic from the appearance of the page
 Allowing fast development and testing

© Trendz Information Technologies Ltd. Page No. 64 of 220


J2EE

 Simplifying the process of developing interactive web-based applications

The Java Server Pages (JSP) technology was designed to fit this need. The JSP
specification is the result of extensive industry cooperation between vendors of web
servers, application servers, transactional systems, and development tools. Sun
Microsystems developed the specification to integrate with and leverage existing
expertise and tools support for the Java programming environment, such as Java
Servlets and JavaBeans. The result is a new approach to developing web-based
applications that extends powerful capabilities to page designers using component-
based application logic.

Java Server Pages Technology approach to Web Application Development


In developing the JSP specification, Sun Microsystems worked with a number of
leading web server, application server and development tool vendors, as well as a
diverse and experienced development community. The result is an approach that
balances portability with ease-of-use for application and page developers.

JSP technology speeds the development of dynamic web pages in a number of ways:

 Separating content generation from presentation


Using JSP technology, web page developers use HTML or XML tags to design
and format the results page. They use JSP tags or scriptlets to generate the
dynamic content (the content that changes according to the request, such as
requested account information or the price of a specific bottle of wine). The
logic that generates the content is encapsulated in tags and JavaBeans
components and tied together in scriptlets, all of which are executed on the
server side. If the core logic is encapsulated in tags and beans, then other
individuals, such as web masters and page designers, can edit the JSP page
without affecting the generation of the content.
On the server, a JSP engine interprets JSP tags and scriptlets, generates
content (for example, by accessing JavaBeans components, accessing a
database with JDBCTM technology, or including files), and sends the results
back in the form of an HTML (or XML) page to the browser. This helps authors
protect proprietary code while ensuring complete portability for any HTML-
based web browser.

 Emphasizing reusable components


Most JSP pages rely on reusable, cross-platform components (JavaBeans or
Enterprise JavaBeans components) to perform the more complex processing
required of the application. Developers can share and exchange components
that perform common operations, or make them available to larger user or
customer communities. The component-based approach speeds overall
development and lets organizations leverage their existing expertise and
development efforts for optimal results.

 Simplifying page development with tags


Web page developers are not always programmers familiar with scripting
languages. The JavaServer Pages technology encapsulates much of the
functionality required for dynamic content generation in easy-to-use, JSP-
specific XML tags. Standard JSP tags can access and instantiate JavaBeans
components, set or retrieve bean attributes, download applets, and perform
other functions that are otherwise more difficult and time-consuming to code.

© Trendz Information Technologies Ltd. Page No. 65 of 220


J2EE

The JSP technology is extensible through the development of customized tag


libraries. Over time, third-party developers and others will create their own
tag libraries for common functions. This lets web page developers work with
familiar tools and constructs, such as tags, to perform sophisticated functions.

The JSP technology integrates easily into a variety of application architectures,


leveraging existing tools and skills, and scaling to support enterprise-wide distributed
applications. As part of the Java technology-enabled family, and an integral part of
the Java 2, Enterprise Edition architecture, the JSP technology can support highly
complex web-based applications.

 Because the native scripting language for JSP pages is based on the Java
programming language, and because all JSP pages are compiled into Java
Servlets, JSP pages have all of the benefits of Java technology, including
robust memory management and security.
 As part of the Java platform, JSP shares the Write Once, Run Anywhere TM
characteristics of the Java programming language. As more vendors add JSP
support to their products, you can use servers and tools of your choice,
changing tools or servers without affecting current applications.
 When integrated with the Java 2 Platform, Enterprise Edition (J2EE) and
Enterprise JavaBeans technology, JSP pages will provide enterprise-class
scalability and performance necessary for deploying web-based applications
across the virtual enterprise.

What Does a JSP Page Look Like?


A JSP page looks like a standard HTML or XML page, with additional elements that
the JSP engine processes and strips out. Typically, the JSP elements create text that
is inserted into the results page.

The JSP technology is best described using an example. The following JSP page is
very simple; it prints the day of the month and the year, and welcomes you with
either "Good Morning" or "Good Afternoon," depending on the time of day.

The page combines ordinary HTML with a number of JSP elements:

 Calls to a clock JavaBean component


 An inclusion of an external file (for copyright information)
 JSP expressions and scriptlets

<HTML>
<%@ page language=="java" imports=="com.wombat.JSP.*" %>

<H1>Welcome</H1>

<P>Today is </P>
<jsp:useBean id=="clock" class=="calendar.jspCalendar" />
<UL>
<LI>Day: <%==clock.getDayOfMonth() %>
<LI>Year: <%==clock.getYear() %>
</UL>

<% if (Calendar.getInstance().get(Calendar.AM_PM) ==== Calendar.AM) { %>


Good Morning

© Trendz Information Technologies Ltd. Page No. 66 of 220


J2EE

<% } else { %>


Good Afternoon
<% } %>
<%@ include file=="copyright.html" %>

</HTML>
The page includes the following components:

 A JSP directive passes information to the JSP engine. In this case, the first
line indicates the location of some Java programming language extensions to
be accessible from this page. Directives are enclosed in <%@ and %>
markers.
 Fixed template data: Any tags that the JSP engine does not recognize it
passes on with the results page. Typically, these will be HTML or XML tags.
This includes the Unordered List and H1 tags in the example above.
 JSP actions, or tags: These are typically implemented as standard tags or
customized tags, and have an XML tag syntax. In the example, the
jsp:useBean tag instantiates the Clock JavaBean on the server.
 An expression: The JSP engine evaluates anything between <%== and %>
markers. In the List Items above, the values of the Day and Year attributes of
the Clock bean are returned as a string and inserted as output in the JSP file.
In the example above, the first list item will be the day of the year, and the
second item the year.
 A scriptlet is a small script that performs functions not supported by tags or
ties everything together. The native scripting language for JSP 1.0 software is
based on the Java programming language. The scriptlet in the above sample
determines whether it is AM or PM and greets the user accordingly (for
daytime users, at any rate).

The example may be trivial, but the technology is not. Businesses can encapsulate
critical processing in server-side Beans, and web developers can easily access that
information, using familiar syntax and tools. Java-based scriptlets provide a flexible
way to perform other functions, without requiring extensive scripting.

JSP Directives
JSP pages use JSP directives to pass instructions to the JSP engine. These may
include the following:

 JSP page directives communicate page-specific information, such as buffer


and thread information or error handling.
 The include directive (shown in the example above) can be used to include an
external document in the page. A good example is a copyright file or company
information, file -- it is easier to maintain this file in one central location and
include it in several pages than to update it in each JSP page. However, the
included file can also be another JSP file.
 A taglib directive indicates a library of custom tags that the page can invoke.

Scripting Elements
JSP pages can includes include small scripts, called scriptlets, in a page. A scriplet is
a code fragment, executed at request time processing. Scriptlets may be combined
with static elements on the page (as in the example above) to create a dynamically
generated page.

© Trendz Information Technologies Ltd. Page No. 67 of 220


J2EE

Scripts are delineated within <% and %> markers. The scripting language engine, in
our example the Java virtual machine on the host, will evaluate anything within those
markers.

The JSP specification supports all of the usual script elements, including expressions
and declarations.

Application Models for JSP Pages


A JSP page is executed by a JSP engine, which is installed in a web server or a JSP-
enabled application server. The JSP engine receives requests from a client to a JSP
page, and generates responses from the JSP page to the client.

JSP pages are typically compiled into Java Servlets. Java Servlets are a standard
Java extension. The page developer has access to the complete Java application
environment, with all of the scalability and portability of the Java technology-enabled
family.

When a JSP page is first called, if it does not yet exist, it is compiled into a Java
Servlet class and stored in the server memory. This enables very fast responses for
subsequent calls to that page. (This avoids the CGI-bin problem of spawning new
processes for each HTTP request, or the runtime parsing required by server-side
includes.)

JSP pages may be included in a number of different application architectures or


models. JSP pages may be used in combination with different protocols, components
and formats. The following sections describe a few of the possibilities.

An Application
In a simple implementation, the browser directly invokes a JSP page, which itself
generates the requested content (perhaps invoking JDBC to get information directly
from a database). The JSP page can call JDBC components to generate results, and
creates standard HTML that it sends back to the browser as a result.

This model basically replaces the CGI-BIN concept with a JSP page (compiled as a
Java Servlet). This method has the following advantages:

 It is simple and fast to program


 The page author can easily generate dynamic content based on the request
and state of the resources.

This architecture works well for many applications, but it does not scale for a large
number of simultaneous Web-based clients accessing scarce enterprise resources,
since each must establish or share a connection to the content resource in question.
For example, if the JSP page accesses a database, it may generate many connections
to the database, which can affect the database performance.

A flexible application with Java Servlets


In another possible configuration, the Web-based client may make a request directly
to a Java Servlet, which actually generates the dynamic content, wraps the results

© Trendz Information Technologies Ltd. Page No. 68 of 220


J2EE

into a result bean and invokes the JSP page. The JSP page accesses the dynamic
content from the bean and sends the results (as HTML) to the browser.

This approach creates more reusable components that can be shared between
applications, and may be implemented as part of a larger application. It still has
scalability issues in terms of handling connections to enterprise resources, such as
databases.

JSP Tags
Most JSP processing will be implemented through JSP-specific XML-based tags. JSP
1.0 includes a number of standard tags, referred to as the core tags. These include:

• jsp:useBean This tag declares the usage of an instance of a


JavaBeans component. If the Bean does not already exist, then
the JavaBean component instantiates and registers the tag.
• jsp:setProperty This sets the value of a property in a Bean.
• jsp:getProperty This tag gets the value of a Bean instance
property, converts it to a string, and puts It in the implicit
object "out".
• jsp:include
• jsp:forward

The advantage of tags is that they are easy to use and share between applications.
The real power of a tag-based syntax comes with the development of custom tag
libraries, in which tool vendors or others can create and distribute tags for specific
purposes.

This set of lines describes the standard actions of JavaServer Pages.Standard actions
are represented using XML elements with a prefix of jsp (though that prefix can be
redefined in the XML syntax). A translation error will result if the JSP prefix is used
for an element that is not a standard action.

<jsp:useBean>
A jsp:useBean action associates an instance of a Java programming language object
defined within a given scope and available with a given id with a newly declared
scripting variable of the same id.When a <jsp:useBean> action is used in an
scriptless page, or in an scriptless context (as in the body of an action so indicated),
there are no Java scripting variables created but instead a variable is created.

The jsp:useBean action is quite flexible; its exact semantics depends on the
attributes given. The basic semantic tries to find an existing object using id and
scope. If the object is not found it will attempt to create the object using the other
attributes.

It is also possible to use this action to give a local name to an object defined
elsewhere, as in another JSP page or in a servlet. This can be done by using the type
attribute and not providing class or beanName attributes.

At least one of type and class must be present, and it is not valid to provide both
class and beanName. If type and class are present, class must be assignable to type
(in the Java platform sense). For it not to be assignable is a translation time error.

© Trendz Information Technologies Ltd. Page No. 69 of 220


J2EE

The attribute beanName specifies the name of a Bean, as specified in the JavaBeans
specification. It is used as an argument to the instantiate method in the
java.beans.Beans class. It must be of the form a.b.c, which may be either a class, or
the name of a resource of the form a/b/c.ser that will be resolved in the current
ClassLoader. If this is not true, a request-time exception, as indicated in the
semantics of the instantiate method will be raised. The value of this attribute can be
a request-time attribute expression.

The id Attribute
The id=”name” attribute/value tuple in a jsp:useBean action has special meaning to
a JSP container, at page translation time and at client request processing time. In
particular:

 The name must be unique within the translation unit, and identifies the
particular element in which it appears to the JSP container and page.
Duplicate id’s found in the same translation unit shall result in a fatal
translation error.
 The JSP container will associate an object (a JavaBean component) with the
named value and accessed via that name in various contexts through the
pagecontext object described later in this specification. The name is also used
to expose a variable (name) in the page’s scripting language environment.
The scope of the scripting language variable is dependent upon the scoping
rules and capabilities of the scripting language used in the page.

Note that this implies the name value syntax must comply with the variable naming
syntax rules of the scripting language used in the page. Provides details for the case
where the language attribute is java.

An example of the scope rules just mentioned is shown next:


<jsp:useBean>
<% { // introduce a new block %>
...
<jsp:useBean id=”customer” class=”mycom.Customer” />
<%
/*
* the tag above creates or obtains the Customer Bean
* reference, associates it with the name “customer” in the
* PageContext, and declares a Java programming language
* variable of the same name initialized to the object reference
* in this block’s scope.
*/
%>
...
<%= customer.getName(); %>
...
<% } // close the block %>
<%
// the variable customer is out of scope now but
// the object is still valid (and accessible via pageContext)
%>

The scope Attribute

© Trendz Information Technologies Ltd. Page No. 70 of 220


J2EE

The scope=”page|request|session|application” attribute/value tuple is associated


with, and modifies the behavior of the id attribute described above (it has both
translation time and client request processing time semantics). In particular it
describes the namespace, the implicit lifecycle of the object reference associated
with the name, and the APIs used to access this association. For all scopes, it is
illegal to change the instance object so associated, such that its new runtime type is
a subset of the type(s) of the object previously so associated.

The actions performed in a jsp:useBean action are:

 If the object is found, the variable’s value is initialized with a reference to the
located object, cast to the specified type. If the cast fails, a
java.lang.ClassCastException shall occur. This completes the processing of this
jsp:useBean action.
 If the jsp:useBean action had a non-empty body it is ignored. This completes
the processing of this jsp:useBean action.
 If the object is not found in the specified scope and neither class nor
beanName are given, a java.lang.InstantiationException shall occur. This
completes the processing of this jsp:useBean action.
 If the object is not found in the specified scope, and the class specified names
a non-abstract class that defines a public no-args constructor, then the class
is instantiated. The new object reference is associated with the scripting
variable and with the specified name in the specified scope using the
appropriate scope dependent association mechanism (sees PageContext).
After this, step 8 is performed. If the object is not found, and the class is
abstract, an interface, or no public no-args constructor is defined therein,
then a java.lang.InstantiationException shall occur. This completes the
processing of this jsp:useBean action.
 If the object is not found in the specified scope; and beanName is given, then
the method instantiate of java.beans.Beans will be invoked with the
ClassLoader of the servlet object and the beanName as arguments. If the
method succeeds, the new object reference is associated the with the
scripting variable and with the specified name in the specified scope using the
appropriate scope dependent association mechanism (see PageContext). After
this, step 8 is performed.
 If the jsp:useBean action has a non-empty body, the body is processed. The
variable is initialized and available within the scope of the body. The text of
the body is treated as elsewhere. Any template text will be passed through to
the out stream. Scriptlets and action tags will be evaluated. A common use of
a non-empty body is to complete initializing the created instance. In that case
the body will likely contain jsp:setProperty actions and scriptlets that are
evaluated. This completes the processing of this useBean action.

Examples
In the following example, a Bean with name connection of type
mycom.myapp.Connection is available after actions on this element, either because it
was already created and found, or because it is newly created.

<jsp:useBean id=”connection” class=”mycom.myapp.Connection” />


In the next example, the timeout property is set to 33 if the Bean was instantiated.

<jsp:useBean id=”connection” class=”mycom.myapp.Connection”>

© Trendz Information Technologies Ltd. Page No. 71 of 220


J2EE

<jsp:setProperty name=”connection” property=”timeout” value=”33”>


</jsp:useBean>

In the final example, the object should have been present in the session. If so, it is
given the local name wombat with WombatType. A ClassCastException may be raised
if the object is of the wrong class, and an InstantiationException may be raised if the
object is not defined.

<jsp:useBean id=”wombat” type=”my.WombatType” scope=”session”/>

Syntax

This action may or not have a body. If the action has no body, it is of the form:

<jsp:useBean id="name" scope="page|request|session|application" typeSpec />


typeSpec ::= class=”className” |
class=”className” type=”typeName” |
type=”typeName” class=”className” |
beanName=”beanName” type=”typeName” |
type=”typeName” beanName=”beanName” |
type=”typeName”

If the action has a body, it is of the form:


<jsp:useBean id="name" scope="page|request|session|application" typeSpec >
body
</jsp:useBean>

In this case, the body will be invoked if the Bean denoted by the action is created.
Typically, the body will contain either scriptlets or jsp:setProperty tags that will be
used to modify the newly created object, but the contents of the body are not
restricted.

<jsp:setProperty>
The jsp:setProperty action sets the values of properties in a bean. The name
attribute that denotes the bean must be defined before this action appears. There
are two variants of the jsp:setProperty action. Both variants set the values of one or
more properties in the bean based on the type of the properties. The usual bean
introspection is done to discover what properties are present, and, for each, its
name, whether it is simple or indexed, its type, and the setter and getter methods.
Introspection also indicates if a given property type has a PropertyEditor class.
Properties in a Bean can be set from one or more parameters in the request object,
from a String constant, or from a computed request-time expression. Simple and
indexed properties can be set using jsp:setProperty.

When assigning values to indexed properties the value must be an array; the rules
described in the previous paragraph apply to the actions. A conversion failure leads
to an error, whether at translation time or request time.

Examples
The following two actions set a value from the request parameter values.
<jsp:setProperty name=”request” property=”*” />
<jsp:setProperty name=”user” property=”user” param=”username” />

© Trendz Information Technologies Ltd. Page No. 72 of 220


J2EE

The following two element set a property from a value


<jsp:setProperty name=”results” property=”col” value=”${i mod 4}”/>
<jsp:setProperty name=”results” property=”row” value=”<%= i/4 %>” />

<jsp:getProperty>
The <jsp:getProperty> action places the value of a bean instance property,
converted to a String, into the implicit out object, from which the value can be
displayed as output. The bean instance must be defined as indicated in the name
attribute before this point in the page (usually via a jsp:useBean action). The
conversion to String is done as in the println methods, i.e. the toString
method of the object is used for Object instances, and the primitive types are
converted directly.

If the object is not found, a request-time exception is raised.

jsp:setProperty Attributes
Name - The name of a bean instance defined by a <jsp:useBean> action or some
other action. The bean instance must contain the property to be set. The defining
action must appear before the <jsp:setProperty> action in the same file.

Property - The name of the property whose value will be set. If property Name is set
to * then the tag will iterate over the current ServletRequest parameters, matching
parameter names and value type(s) to property names and setter method type(s),
setting each matched property to the value of the matching parameter. If a
parameter has a value of "", the corresponding property is not modified.

Param - The name of the request parameter whose value is given to a bean property.
The name of the request parameter usually comes from a web form. If param is
omitted, the request parameter name is assumed to be the same as the bean
property name. If the param is not set in the Request object, or if it has the value of
““, the jsp:setProperty action has no effect.. An action may not have both param and
value attributes. value The value to assign to the given property. This attribute can
accept a request-time attribute expression as a value.An action may not have both
param and value attributes.

The value of the name attribute in jsp:setProperty and jsp:getProperty will refer to
an object that is obtained from the pageContext object through its findAttribute
method. The object named by the name must have been “introduced” to the JSP
processor using either the jsp:useBean action or a custom action with an associated
VariableInfo entry for this name. If the object was not introduced in this manner, the
container implementation is recommended (but not required) to raise a translation
error.

Note – A consequence of the previous paragraph is that objects that are stored in,
say, the session by a front component are not automatically visible to jsp:set-
Property and jsp:getProperty actions in that page unless a jsp:useBean action, or
some other action, makes them visible.

If the JSP processor can ascertain that there is an alternate way guaranteed to
access the same object, it can use that information. For example it may use a
scripting variable, but it must guarantee that no intervening code has invalidated the
copy held by the scripting variable. The truth is always the value held by the
pageContext object.

© Trendz Information Technologies Ltd. Page No. 73 of 220


J2EE

Examples

<jsp:getProperty name=”user” property=”name” />

<jsp:include>
A <jsp:include .../> action provides for the inclusion of static and dynamic resources
in the same context as the current page. Inclusion is into the current value of out.
The resource is specified using a relativeURLspec that is interpreted in the context of
the web application (i.e. it is mapped).

The page attribute of both the jsp:include and the jsp:forward actions are interpreted
relative to the current JSP page, while the file attribute in an include directive is
interpreted relative to the current JSP file. See below for some examples of
combinations of this.

An included page cannot change the response status code or set headers. This
precludes invoking methods like setCookie. Attempts to invoke these methods will be
ignored. The constraint is equivalent to the one imposed on the include method of
the RequestDispatcher class.

A jsp:include action may have jsp:param subelements that can provide values for
some parameters in the request to be used for the inclusion. Request processing
resumes in the calling JSP page, once the inclusion is completed.

The flush attribute controls flushing. If true, then, if the page output is buffered and
the flush attribute is given a true value, then the buffer is flushed prior to the
inclusion, otherwise the buffer is not flushed. The default value for the flush attribute
is false.

Examples
<jsp:include page=”/templates/copyright.html”/>
The above example is a simple inclusion of an object. The path is interpreted in the
context of the Web Application. It is likely a static object, but it could be mapped
into, for instance, a servlet via web.xml. For an example of a more complex set of
inclusions, consider the following four situations built using four JSP files: A.jsp,
C.jsp, dir/B.jsp and dir/C.jsp:

 A.jsp says <%@ include file=”dir/B.jsp”%> and dir/B.jsp says <%@ include
file=”C.jsp”%>. In this case the relative specification C.jsp resolves to
dir/C.jsp.
 A.jsp says <jsp:include page=”dir/B.jsp”/> and dir/B.jsp says <jsp:include
page=”C.jsp” />. In this case the relative specification C.jsp resolves to dir/
C.jsp.
 A.jsp says <jsp:include page=”dir/B.jsp”/> and dir/B.jsp says <%@ include
file=”C.jsp” %>. In this case the relative specification C.jsp resolves to
dir/C.jsp.
 A.jsp says <%@ include file=”dir/B.jsp”%> and dir/B.jsp says <jsp:include
page=”C.jsp”/>. In this case the relative specification C.jsp resolves to C.jsp.

<jsp:forward>

© Trendz Information Technologies Ltd. Page No. 74 of 220


J2EE

A <jsp:forward page=”urlSpec” /> action allows the runtime dispatch of the current
request to a static resource, a JSP page or a Java servlet class in the same page The
URL is a relative urlSpec as in Section.

Relative paths are interpreted relative to the current JSP page. Accepts a request-
time attribute value (which must evaluate to a String that is a relative URL
specification). flush Optional boolean attribute. If the value is true, the buffer is
flushed now. The default value is false.

<jsp:forward>
A jsp:forward effectively terminates the execution of the current page. The relative
urlSpec.

The request object will be adjusted according to the value of the page attribute.

A jsp:forward action may have jsp:param subelements that can provide values for
some parameters in the request to be used for the forwarding.

If the page output is buffered, the buffer is cleared prior to forwarding.

If the page output is buffered and the buffer was flushed, an attempt to forward the
request will result in an IllegalStateException. If the page output was unbuffered and
anything has been written to it, an attempt to forward the request will result in an
IllegalStateException.

Examples
The following action might be used to forward to a static page based on some
dynamic condition.
<% String whereTo = “/Trendz/”+someValue; %>
<jsp:forward page=’<%= whereTo %>’ />
Syntax
<jsp:forward page=”relativeURLspec” />
and
<jsp:forward page=”urlSpec”>
{ <jsp:param .... /> }*
</jsp:forward>

<jsp:param>
The jsp:param element is used to provide key/value information. This element is
used in the jsp:include, jsp:forward, and jsp:params elements. A translation error
shall occur if the element is used elsewhere.

When doing jsp:include or jsp:forward, the included page or forwarded page will see
the original request object, with the original parameters augmented with the new
parameters, with new values taking precedence over existing values when applicable.
The scope of the new parameters is the jsp:include or jsp:forward call; i.e. in the
case of an jsp:include the new parameters (and values) will not apply after the
include. This is the same behavior as in the ServletRequest include and forward
methods.

Syntax

© Trendz Information Technologies Ltd. Page No. 75 of 220


J2EE

<jsp:param name="name" value="value" />

This action has two mandatory attributes: name and value. name indicates the name
of the parameter, and value, which may be a request-time expression, indicates its
value.

<jsp:plugin>
The plugin action enables a JSP page author to generate HTML that contains the
appropriate client browser dependent constructs (OBJECT or EMBED) that will result
in the download of the Java Plugin software (if required) and subsequent execution of
the Applet or JavaBeans component specified therein.

The <jsp:plugin> tag is replaced by either an <object> or <embed> tag, as


appropriate for the requesting user agent, and emitted into the output stream of the
<jsp:plugin> response. The attributes of the <jsp:plugin> tag provide configuration
data for the presentation of the element, as indicated in the table below.

Examples
<jsp:plugin type=”applet” code=”MyApplet.class” codebase=”/html” >
<jsp:params>
<jsp:param name=”Trendz” value=”People Committed to Quality”/>
</jsp:params>
</jsp:plugin>

CUSTOM TAG LIBRARY

What is a Tag Library?


In JavaServer Pages technology, actions are elements that can create and access
programming language objects and affect the output stream. The JSP specification
defines 6 standard actions that must be provided by any compliant JSP
implementation.

In addition to the standard actions, JSP v1.1 technology supports the development of
reusable modules called custom actions. A custom action is invoked by using a
custom tag in a JSP page. A tag library is a collection of custom tags.

Some examples of tasks that can be performed by custom actions include form
processing, accessing databases and other enterprise services such as email and
directories, and flow control. Before the availability of custom actions, JavaBeans
components in conjunction with scriplets were the main mechanism for performing
such processing. The disadvantage of using this approach is that it makes JSP pages
more complex and difficult to maintain.

Custom actions alleviate this problem by bringing the benefits of another level of
componentization to JSP pages. Custom actions encapsulate recurring tasks so that
they can be reused across more than one application and increase productivity by
encouraging division of labor between library developers and library users. JSP tag
libraries are created by developers who are proficient at the Java programming
language and expert in accessing data and other services. JSP tag libraries are used
by Web application designers who can focus on presentation issues rather than being
concerned with how to access databases and other enterprise services.

Some features of custom tags are:

© Trendz Information Technologies Ltd. Page No. 76 of 220


J2EE

• They can be customized via attributes passed from the calling page.
• They have access to all the objects available to JSP pages.
• They can modify the response generated by the calling page.
• They can communicate with each other. You can create and initialize a
JavaBeans component, create a variable that refers to that bean in one tag,
and then use the bean in another tag.
• They can be nested within one another, allowing for complex interactions
within a JSP page.

Declaring Tag Libraries


You declare that a JSP page will use tags defined in a tag library by including a taglib
directive in the page before any custom tag is used:

<%@ taglib uri="/tlt" prefix="tlt" %>

The uri attribute refers to a URI that uniquely identifies the tag library. This URI can
be relative or absolute. If it is relative it must be mapped to an absolute location in
the taglib element of a Web application deployment descriptor, the configuration file
associated with Web applications developed according to the Java Servlet and
JavaServer Pages specifications. The prefix attribute defines the prefix that
distinguishes tags provided by a given tag library from those provided by other tag
libraries.

JSP custom actions are expressed using XML syntax. They have a start tag and end
tag, and possibly a body:
<tlt:tag>
body
</tlt:tag>
A tag with no body can be expressed as follows:
<tlt:tag />

Simple Tags
The following simple tag invokes an action that creates a greeting:
<tlt:greeting />

Tags With Attributes


The start tag of a custom action can contain attributes in the form attr="value".
Attributes serve to customize the behavior of a tag just as parameters are used to
affect the outcome of executing a method on an object.

Tag attributes can be set from one or more parameters in the request object or from
a String constant. The only types of attributes that can be set from request
parameter values and String constants are those listed in Table 1; the conversion
applied is that shown in the table. When assigning values to indexed attributes the
value must be an array; the rules just described apply to the elements.

Table 1 Valid Tag Attribute Assignments


Property Type Conversion on String Value
boolean or Boolean As indicated in java.lang.Boolean.valueOf(String)
byte or Byte As indicated in java.lang.Byte.valueOf(String)

© Trendz Information Technologies Ltd. Page No. 77 of 220


J2EE

char or Character As indicated in java.lang.Character.valueOf(String)


double or Double As indicated in java.lang.Double.valueOf(String)
int or Integer As indicated in java.lang.Integer.valueOf(String)
float or Float As indicated in java.lang.Float.valueOf(String)
long or Long As indicated in java.lang.Long.valueOf(String)

An attribute value of the form <%= scriptlet_expression %> is computed at request


time. The value of the expression depends on the type of the attribute's value, which
is specified in the object that implements the tag (called a tag handler). Request-
time expressions can be assigned to attributes of any type; no automatic conversions
will be performed.

The following tag has an attribute named date, which accepts a String value obtained
by evaluating the variable today:

<tlt:greeting date="<%= today %>" />

The Future for JSP Technology


JSP technology is designed to be an open, extensible standard for building dynamic
web pages. Developers will use JSP pages to create portable web applications that
can run with different web and application servers for different markets, using
whatever authoring tools fit their market and their needs.

By working with a consortium of industry leaders, Sun has ensured that the JSP
specification is open and portable. You should be able to author JSP pages anywhere
and deploy them anywhere, using any client and server platforms. Over time, tool
vendors and others will extend the functionality of the platform by providing
customized tag libraries for specialized functions.

© Trendz Information Technologies Ltd. Page No. 78 of 220


J2EE

 JNDI
• Naming & Directory Service
• Installing OpenLDAP
• Accessing Naming Service
• Accessing Directory Service
 Java Transaction API
• Transaction Service
• Bean managed Transaction
 Java Mail API
• Java Mail & JAF
• Sample Application
 EJB
• Overview
• Remote & Home Interfaces
• Entity Beans with CMP and BMP
• Session Bean as Stateless and Stateful
• EJB 2.0 Features
• EJB QL
• Message-Driven Bean
• EJB & WebService
 J2EE Design Pattern
• Model-view-Controller
• What is Design Pattern
• Helpful Hints

© Trendz Information Technologies Ltd. Page No. 79 of 220


J2EE

JAVA 2 EDITIONS

Basically Java2 Editions like

 J2SE (Java 2 Standard Edition)


Java 2 Platform, Standard Edition (J2SE) provides a complete environment for
applications development on desktops and servers. It also serves as the
foundation for the Java 2 Platform, Enterprise Edition (J2EE) and Java Web
Services.

 J2EE (Java 2 Enterprise Edition)


Java 2 Platform, Enterprise Edition (J2EE) defines the standard for developing
component-based multi-tier enterprise applications. Features include Web
services support and development tools.

 J2ME (Java 2 Micro Edition)


The Micro Edition of the Java 2 Platform provides an application environment
that specifically addresses the needs of commodities in the vast and rapidly
growing consumer and embedded space, including mobile phones, pagers,
personal digital assistants, set-top boxes, and vehicle telematics systems.

J2EE Architecture

 The J2EE platform uses a multi-tiered distributed application model for both
enterprise applications
 Application logic is divided into “components” according to function, and the
various application components that make up a J2EE application are installed
on different machines depending on the tier in the multi-tiered J2EE
environment to which the application component belongs
 J2EE multi-tiered applications are generally considered to be three-tiered
applications because they are distributed over three different locations
 Client machines
 The J2EE server machine
 The database or legacy machines at the back end
 Three-tiered applications that run in this way extend the standard two-tiered
client and server model by placing a multithreaded application server between
the client application and back-end storage

J2EE Containers

 The Application Server maintains control and provides services through an


interface or framework known as a container
 There are five defined container types in the J2EE specification
 Three of these are server-side containers:
 The server itself, which provides the J2EE runtime environment and the other
two containers
 An EJB container to manage EJB components
 A Web container to manage Servlets and JSP pages
 The other two container types are client-side:
 An application container for Stand-alone GUIs, Console
 An applet container, meaning a browser, usually with the Java Plug-in

© Trendz Information Technologies Ltd. Page No. 80 of 220


J2EE

J2EE ARCHITECTURE

JAVA NAMING & DIRECTORY INTERFACE

Lightweight Directory Access Protocol (LDAP) is increasingly popular because it


simplifies what has been complex, namely, accessing directory services. In this
article you will learn what you can do with LDAP and how Java handles LDAP with its
Java Naming and Directory Interface (JNDI) API. At the end of the article, you will
find a project that provides a white pages service built with LDAP and JNDI.

Naming and Directory Services


Before you start the project, you should first familiarize yourself the jargon of
naming and directory services.

The Naming Service


A Naming Service lets you find an object in a system based on the name associated
with the object. Naming Services are easy to find. Take the file system in your
computer, for instance. Every file in a computer has a name; to access a file you
must know its name. In file systems, files are objects associated with filenames.
Another example is the Internet's DNS, which maps easy-to-remember names (such
as onjava.com and jUpload.com) to IP addresses. When you work with Enterprise
JavaBeans (EJB), you use a naming service to get a reference to a Bean. In short, a
naming service allows you to look up an object by its name.

© Trendz Information Technologies Ltd. Page No. 81 of 220


J2EE

Each naming service has its own rules for making valid names. For example, the
rules for valid filenames Linux are different from the rules in Windows.

The association of a name with an object is called a binding. In a file system, a


filename is bound to a file. In a DNS server, domain names are bound to IP
addresses.

Objects in some naming services cannot be stored directly inside the naming service.
Instead, the name service stores pointers or references to objects. A reference
contains an address, that is, specific information on how to access the object itself.

A Context
In a naming service, obviously you have more than one name-to-object binding. The
set of bindings is called a context. There are two types of contexts: root and
subcontext. A root context is the base name of an object. In a file system, the root
context is the base from which all other directories and files are stored. In the Unix
file system, the root context is /. Under Windows it is normally C:\.

A subcontext is a name that adds another level to the root context. For example, a
directory, such as ‘usr’ under / in a Unix file system, is a subcontext. In the Unix
system, this subcontext is called a subdirectory. That is, in a directory, /usr, the
directory usr is a subcontext of /. In another example, a DNS domain, like COM or
NET, is a context. A DNS domain named relative to another DNS domain is a
subcontext. For example, in the DNS domain brainysoftware.com, the DNS domain
brainysoftware is a subcontext of COM.

The Directory Service


A Directory Service is an extension of a Naming Service. In a Directory Service, an
object is also associated with a name. However, each object is allowed to have
attributes. You can look up an object by its name; but you can also obtain the
object's attributes or search for the object based on its attributes.

Going back to the Unix file system, it's not just a naming service but also a directory
service. Each file can have attributes like owner and date. In real world applications,
a directory object in a directory server can be used to represent anything: a printer,
a computer, a network, or even a person in an organization.

Attributes
An attribute of a directory object is a property of the object. For example, a person
can have the following attributes: Last name, First name, User name, email address,
Telephone number, and so on. A printer can have attributes like Resolution, Color,
and Speed.

An attribute has an identifier, which is a unique name in that object. Each attribute
can have one or more values. For instance, a person object can have an attribute
called LastName. The LastName is the identifier of an attribute. An attribute value is
the content of the attribute. For example, the LastName attribute can have a value
like "Martin".

Searches and Search Filters


You can look up a directory object by supplying its name to a directory service.
Alternatively, many directory services support searches for objects based on
properties, not just names. You can supply a query consisting of a logical expression

© Trendz Information Technologies Ltd. Page No. 82 of 220


J2EE

in which you specify the attributes that the object or objects must have. The query is
called a search filter. This style of searching is sometimes called reverse lookup or
content-based searching. The directory service searches for and returns the objects
that satisfy the search filter.

LDAP
Directory services are very common these days. There already exist a plethora of
directory service implementations:

• Novell Directory Service (NDS)


• Network Information Services (NIS/NIS+)
• Windows NT Domain Naming Service (DNS)
• Active Directory Services (ADS)

Accessing a directory service and manipulating its objects used to be complex and
difficult. The traditional protocol is X.500, a set of directory recommendations
specified by the International Telecommunication Union. X.500 was enormous and
complex.

LDAP is a direct descendant of X.500. LDAP was designed at the University of


Michigan to simplify access to X.500 directories (hence the "L" for "lightweight" in
"LDAP"). LDAP was designed to be powerful enough to solve basic problems in
accessing a directory service but simple and light enough so more people can afford
to use it.

LDAP, currently at version 3, is now a standard for directory information access.


Many companies, including Microsoft, IBM, Novell, Computer Associates, and Sun,
have agreed to support it. LDAP is now being used as an important part of a variety
of services: authentication systems, mail systems, and e-commerce applications. To
date more than 60 LDAP server implementations have been released; approximately
90% of which are standalone LDAP directory servers, 10% of which are components
of other applications.

You probably already have an LDAP-aware client installed on your computer. Many
email clients can access an LDAP directory for email addresses, including Outlook,
Eudora, Netscape Communicator, QuickMail Pro, and Mulberry.

The LDAP naming convention orders components from right to left, delimited by a
comma. LDAP arranges all directory objects in a tree, called a Directory Information
Tree (DIT). Within the DIT, an organization object, for example, might contain group
objects that might in turn contain person objects. When directory objects are
arranged in this way, they play the role of naming contexts in addition to being
attribute containers.

Standard LDAP Operations


In addition to client connections and disconnections, an LDAP server must provide
the following:

• The ability to bind to the LDAP server;


• The ability to add, modify and delete an entry; and
• The ability to search the server for these entries.

© Trendz Information Technologies Ltd. Page No. 83 of 220


J2EE

Note that the term binding in LDAP is different from its generic directory services
meaning. Binding here refers to the authentication that a user is required to perform
before accessing an entry in the directory.

Public LDAP Servers


There are several public LDAP servers you can use over the Internet; the popular of
these is probably BigFoot's (ldap://ldap.bigfoot.com); others include
ldap://ldap.four11.com and ldap://ldap.InfoSpace.com.

A number of universities in the US also provides LDAP service to search for students
or staff members. For a list of university public LDAP services, see eMailman's Public
LDAP Servers.

Choosing an LDAP Server


Your organization may already run a directory service, especially if it is very large. If
not so, you probably need to do some research before deciding on one.

The most popular LDAP server today is iPlanet's Directory Server. Others include
Novell's NDS eDirectory, Critical Path's Global Directory Server, Computer Associates'
eTrust Directory, Siemens' DirX, and Oracle's Oracle Internet Directory. Deciding the
one, which is best for your situation is often tricky.

NetworkWorld Fusion published a good article last year, which compares the
performance of many LDAP servers. If it's to be believed, iPlanet is the best
performer and also the fastest; it concludes that iPlanet's Directory Server is the best
choice for commercial use.

If you only need an LDAP server for testing, you probably want to use something
else. Downloads for the latest version of iPlanet's Directory Server (version 5.0 beta)
range from 53 MB to 78 MB, depending on your operating system. For the project in
this article, I chose the much slimmer LDAP server from OpenLDAP. Even though not
the fastest, their free product is only a 1.52 MB download. OpenLDAP's products are
only available for Linux; but once you have seeded it with entries, you can use this
article's project code to access any LDAP server on any operating system.

Installing OpenLDAP
You can download OpenLDAP from the project's site. The LDAP server is called slapd
(a stand-alone LDAP server). The latest version of slapd is 2.0.7. Other programs
downloadable from the Web site are the replication server, some libraries, and a
variety of tools.

To install slapd, you first need to download openldap-2_0_7.tgz into the /usr/local/
directory of a Linux system. You can use another directory but you'll need to do
some adjustment to the following instructions.

Installation takes the following steps:

1. Extract the files --


gunzip -c openldap-2_0_7.tgz | tar xvfB -
-- into a subdirectory, openldap-2.0.7. If you are using a different version,
this subdirectory is called openldap-VERSION.
2. Assuming that your current working directory is /usr/local, run
cd openldap-2.0.7

© Trendz Information Technologies Ltd. Page No. 84 of 220


J2EE

3. Next, run
./configure
4. Then the following commands:
5. make depend
6. make
make test
7. If everything goes smoothly, you are now ready to install, for which you'll
need root access. Run
su root -c 'make install'

Configuring slapd
If installation is as expected, you are now ready to configure slapd. The configuration
file is called slapd.conf and can be found at the /usr/local/etc/openldap/ directory.
Open this file with your favorite text editor.

You should see the following lines.

database ldbm
suffix "dc=<MY-DOMAIN>,dc=<COM>"
rootdn "cn=Manager,dc=<MY-DOMAIN>,dc=<COM>"
rootpw secret
directory /usr/local/var/openldap-ldbm

You need to edit the <MY-DOMAIN> and the <COM> parts to reflect your domain
name. Using the correct names ensures that your LDAP server can be accessed from
the Internet.

For example, for the brainysoftware.com domain, the configuration lines will look like
database ldbm
suffix "dc=brainysoftware,dc=com"
rootdn "cn=Manager,dc=brainysoftware,dc=com"
rootpw secret
directory /usr/local/var/openldap-ldbm
If your domain contains additional components -- like sandal.jepit.edu.au -- do
something like

database ldbm
suffix "dc=sandal,dc=jepit,dc=edu,dc=au"
rootdn "cn=Manager,dc=sandal,dc=jepit,dc=edu,dc=au"
rootpw secret
directory /usr/local/var/openldap-ldbm

The fourth line (rootpw secret) contains the root password that you need to supply to
the server to make changes to the entries and do some other functions.

Running slapd
Running slapd requires root access, so run
su root -c /usr/local/libexec/slapd
or
/usr/local/libexec/slapd
if you're already logged in as root.

© Trendz Information Technologies Ltd. Page No. 85 of 220


J2EE

To check that the server is running and configured correctly, you can search it with
ldapsearch. By default, ldapsearch is installed as /usr/local/bin/ldapsearch. Use the
following command:
ldapsearch -x -b '' -s base '(objectclass=*)' namingContexts

The Schema file


An important file in an LDAP server is the schema file, which, for slapd, is called
core.schema, located at /usr/local/openldap-2.0.7/etc/openldap/schema/. It contains
the directory schema of the LDAP server.

A directory schema specifies, among other things, the types of objects that a
directory may have and the attributes that are mandatory and optional to that
object. A directory schema also contains attribute type definitions, object class
definitions, and other information, which a server uses to determine how to match a
filter or attribute value assertion against the attributes of an entry, and whether to
permit add and modify operations.

The LDAP v3 schema is based on the X.500 standard for common objects found in a
network like countries, localities, organizations, users/persons, groups and devices.

The LDAP v3 schema is specified in RFC 2252 and RFC 2256.

All LDAP entries in the directory are typed. Each entry belongs to object classes that
identify the type of data represented by the entry. The object class specifies the
mandatory and optional attributes that can be associated with an entry of that class.
The object classes for all objects in the directory form a class hierarchy. The classes
top and alias are at the root of the hierarchy. For example, the organizationalPerson
object class is a subclass of the Person object class, which in turn is a subclass of
top. When creating a new LDAP entry, you must always specify all of the object
classes to which the new entry belongs. Because many directories do not support
object class subclassing, you also should always include all of the superclasses of the
entry.

Three types of object classes are possible:

• Structural. Indicates the attributes that the entry may have and where each
entry may occur in the DIT.
• Auxiliary. Indicates the attributes that the entry may have.
• Abstract. Indicates a "partial" specification in the object class hierarchy; only
structural and auxiliary subclasses may appear as entries in the directory.

For example, for an organizationalPerson object, you should list in its object classes
the organizationalPerson, person, and top classes. The organizationalPerson, person,
and top objects are listed as the following entries in the core.schema file.

objectclass ( 2.5.6.0 NAME 'top' ABSTRACT


MUST objectClass )
objectclass ( 2.5.6.6 NAME 'person' SUP top STRUCTURAL
MUST ( sn $ cn )
MAY ( userPassword $ telephoneNumber $ seeAlso $ description )
)
objectclass ( 2.5.6.7 NAME 'organizationalPerson' SUP person
STRUCTURAL

© Trendz Information Technologies Ltd. Page No. 86 of 220


J2EE

MAY ( title $ x121Address $ registeredAddress $


destinationIndicator $ preferredDeliveryMethod $
telexNumber $ teletexTerminalIdentifier $
telephoneNumber $ internationaliSDNNumber $
facsimileTelephoneNumber $ street $ postOfficeBox $
postalCode $ postalAddress $ physicalDeliveryOfficeName $
ou $ st $ l
)
)

LDAP v3 specifies that each directory entry may contain an operational attribute that
identifies its subschema subentry. A subschema subentry contains the schema
definitions for the object classes and attribute type definitions used by entries in a
particular part of the directory tree. If a particular entry does not have a subschema
subentry, then the subschema subentry of the root DSE, which is named by the
empty DN, is used. For more information about the schema, refer to RFCs 2252 and
2256.

Adding Entries
Adding entries to the server is the first thing you should do. To add entries to slapd,
you use ldapadd, which reads the content of an ldif file, checks the validity of its
entries, and adds the entries to the server if the entries are correct.

To add entries to the LDAP server, you need to pass the domain name and the
password for the root user. For example, with the following command you pass the
domain name (sendal.jepit.edu.au) and the password (secret) and the example.ldif
containing the entries to be added.
ldapadd -x -D "cn=Manager ,dc=sendal,dc=jepit,dc=edu,dc=au" -w secret -f
example.ldif
The argument list of ldapadd can be displayed by typing ldapadd with no arguments.

LDAP Data Interchange Format (LDIF)


As mentioned above, the LDIF is used to represent LDAP entries in text form. The
basic syntax of an LDIF entry is
.
[<id>]
dn: <distinguished name>
<attrtype>: <attrvalue>
<attrtype>: <attrvalue>
...
where <id> is the optional entry ID (a positive decimal number). Normally, you
would not supply the <id>, allowing the database creation tools to do that for you. A
line may be continued by starting the next line with a single space or tab character,
as in

dn: cn=Frank Dominic, o=University of Michigan, c=US


Multiple attribute values are specified on separate lines.
cn: Frank Dominic
cn: Frank B Dominic

If an <attrvalue> contains a non-printing character, or begins with a space or a colon


(:), the <attrtype> is followed by a double colon and the value is encoded in base 64
notation. e.g., the value " begins with a space" would be encoded like this:

© Trendz Information Technologies Ltd. Page No. 87 of 220


J2EE

cn:: IGJlZ2lucyB3aXRoIGEgc3BhY2U=
Blank lines separate multiple entries within the same LDIF file.
Here is an example of an LDIF file containing three entries.
dn: cn=Barbara J Jensen, o=University of Michigan, c=US
cn: Barbara J Jensen
cn: Babs Jensen
objectclass: person
sn: Jensen
dn: cn=Bjorn J Jensen, o=University of Michigan, c=US
cn: Bjorn J Jensen
cn: Bjorn Jensen
objectclass: person
sn: Jensen
dn: cn=Jennifer J Jensen, o=University of Michigan, c=US
cn: Jennifer J Jensen
cn: Jennifer Jensen
objectclass: person
sn: Jensen
jpegPhoto:: /9j/4AAQSkZJRgABAAAAAQABAAD/2wBDABALD
A4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQ
ERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVG ...
Notice that the jpegPhoto in Jennifer Jensen's entry is encoded in base 64.

Java Naming and Directory Interface (JNDI)


The JNDI is API for writing programs to access naming and directory services.
The JNDI is grouped into five packages.

• javax.naming
• javax.naming.directory
• javax.naming.event
• javax.naming.ldap
• javax.naming.spi

For the project in this article you only need the javax.naming and
javax.naming.directory packages.

JNDI is included in version 1.3 of Java 2 SDK. If you are using this version, you are
in luck. For users of JDK 1.1 and Java 2 SDK version 1.2, the JNDI can be
downloaded and installed separately. In the Java 2 SDK, version 1.3, you can find
service providers for the following services:

• LDAP
• CORBA Common Object Service (COS) Name Service
• Java Remote Method Invocation (RMI) Registry.

If you are using an older version of Java, you must first download the JNDI as a
Standard Extension on the JDK 1.1 and Java 2 SDK, version 1.2.

You must also download one or more service providers. These service providers act
like JDBC drivers for database access.

© Trendz Information Technologies Ltd. Page No. 88 of 220


J2EE

Accessing A Naming Service


When accessing a naming service, you first need a service provider. The first thing to
do is to get the initial context, which is the starting position into the namespace. You
acquire the initial context before you do any other operation. This is because all
operations on naming and directory services are performed relative to some context.
If you specify that your initial context when accessing a filesystem is the /usr/local
directory when you call the list() method, then it's the contents of the /usr/local
directory that will be returned. You can think of the initial context as the application
default directory.

To obtain the initial context, you call the InitialContext() constructor, passing all the
necessary environment information in a Hashtable object:

Hashtable env = new Hashtable();

Into the Hashtable, you then put the service provider. For example, if you are using
the file system service provider from Sun, this is the line of code you need.

env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.fscontext.RefFSContextFactory");

The file system service provider can be downloaded here. If you are using a
different service provider, replace put()'s second argument.

Another important environment property that you need to get the initial context is
the PROVIDER_URL. This property is assigned the location of the initial context. This
could be a URL on the Internet or it could just be a directory in a file system. For
instance, if you decide that your initial context when accessing a Unix file system is
the /usr/local directory, then you need the following line of code.

env.put(Context.PROVIDER_URL, "file:/usr/local");

Or, on a Windows system, if you want the C:\data directory to be the initial context,
your code would look like the following.

env.put(Context.PROVIDER_URL, "file:C:\\data");

And, optionally, you can also put the user credentials such as the username and
password.

env.put(Context.SECURITY_PRINCIPAL, "james");
env.put(Context.SECURITY_CREDENTIALS, "secret");

Having the environment information ready, you can now create the initial context.
Context ctx = new InitialContext(env);

If the object is created successfully, you can use the resulting Context object to
access the naming service. The lookup method of the Context interface can be used
to retrieve an object by passing its name.

Object obj = ctx.lookup("info.txt");

© Trendz Information Technologies Ltd. Page No. 89 of 220


J2EE

For example, the following code prepares an environment Hashtable object, creates
an initial context, and retrieves the info.txt file.

import java.util.Hashtable;
import javax.naming.*;
import java.io.File;

public class Naming {


public static void main(String[] args) {

Hashtable env = new Hashtable();


env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.fscontext.RefFSContextFactory");
env.put(Context.PROVIDER_URL,
"file:C:\\123data\\MyArticles\\WhitePagesWithLDAP");
env.put(Context.SECURITY_PRINCIPAL, "james");
env.put(Context.SECURITY_CREDENTIALS, "secret");

try {
Context ctx = new InitialContext(env);
File f = (File)ctx.lookup("info.txt");
}
catch (NamingException e) {
System.out.println(e.toString());
}
}
}

The ‘Object’ object from the lookup method is cast to a File object. If the object is a
Printer, you can do something similar:

Printer printer = (Printer) ctx.lookup("BigMomma");


printer.print(report);

Some of the code is in a try-catch wrapper because many methods in the JNDI
packages can throw a NamingException.

Other useful methods of the Context interface include the following.

• bind() -- Binds an object to a name. After the binding, you can retrieve the
object by looking up the name.
• rebind() -- Adds or replaces a binding. If the name is already bound to an
object, it will be unbound and bound with the new object specified as the
argument of this method.
• unbind() -- Removes a binding.
• list() -- Enumerates the names bound in the named context, along with the
class names of objects bound to them.

Every naming method in the Context interface has two overloads: one that accepts a
Name argument and one that accepts a java.lang.String name. Name is an interface
that represents a generic name; an ordered sequence of zero or more components.

© Trendz Information Technologies Ltd. Page No. 90 of 220


J2EE

The overloads that accept Name are useful for applications that need to manipulate
names, that is, composing them, comparing components, and so on.

A java.lang.String name argument represents a composite name. The overloads that


accept java.lang.String names are likely to be more useful for simple applications,
such as those that read in a name and look up the corresponding object.

Accessing A Directory Service


When you access a directory service, there are several initial steps to perform. The
first is to prepare an environment Hashtable object to get the initial context.

Hashtable env = new Hashtable();


One of the environment properties you need to set is the
INITIAL_CONTEXT_FACTORY. For example, if you are accessing an LDAP service, you
can use the service provider from Sun. The code would then look like the following.

env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");

If you are using a service provider from another vendor, just replace the second
argument to put(). Next, you supply the location of the service. For example, the
following specifies a location of an LDAP server at ldap://sendal.jepit.edu.au:389
(389 is the default port for the LDAP service).

env.put(Context.PROVIDER_URL,
"ldap://sendal.jepit.usyd.edu.au:389");

You can then acquire an initial context by passing the environment Hashtable.
However, unlike accessing a naming system, you use the DirContext interface instead
of the Context interface.

DirContext ctx = new InitialDirContext(env);

Having a DirContext object, you can access the directory service using the methods
of the DirContext interface; the important methods of which include getAttributes,
getSchema and search.

• getAttributes() -- Returns the attributes of an object in the directory. For this


method to work, you need to pass the name of the object for which you want
the attributes. If you have an object whose name is "cn=boni, ou=person",
you can retrieve the object's attributes using the following line of code.
Attributes attr = ctx.getAttributes("cn=boni, ou=person");
• getSchema() -- Retrieves the schema associated with the named object.
• search() -- Search for entries in a named context or object. The search must
satisfy a given search filter. The syntax of one of the many search methods is
as follows.
• public NamingEnumeration search(String name, String filter,
SearchControls cons) throws NamingException
The parameters are given below.
o name -- The name of the context or object to search.
o filter -- The filter expression to use for the search; may not be null
o cons -- These control the search. If null, the default search controls
are used (equivalent to (new SearchControls())).

© Trendz Information Technologies Ltd. Page No. 91 of 220


J2EE

You can create a SearchControls object by using the following code.


SearchControls constraints = new SearchControls();
constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);

Developing a White Pages Service


A white pages service for locating a person in an LDAP server. As mentioned
previously, I use the LDAP server from OpenLDAP. In order to keep the project
simple, I use the person object defined in the core.schema file.

For convenience, the person object in the core.schema file is re-presented here.

objectclass ( 2.5.6.6 NAME 'person' SUP top STRUCTURAL


MUST ( sn $ cn )
MAY ( userPassword $ telephoneNumber $ seeAlso $ description )
)

The person object has two mandatory attributes: sn and cn, and four optional
attributes:

• userPassword
• telephoneNumber
• seeAlso
• description

Adding Some Entries


To test the code in this project, you need to populate the directory:

ldapadd -x -D "cn=Manager ,dc=sendal,dc=jepit,dc=edu,dc=au" -w


secret -f example.ldif

This reads the example.ldif file and insert its content as entries to the server. The
example.ldif file contains the following.

dn: cn=Bulbul, dc=sedal,dc=usyd,dc=edu,dc=au


objectclass: person
cn: Bulbul Kurniawan
sn: Kurniawan
userPassword: secret
telephoneNumber: +61 98371313

dn: cn=boni, dc=sedal,dc=usyd,dc=edu,dc=au


objectclass: person
cn: Boni Milliken
sn: Milliken
userPassword: dog
telephoneNumber: +61 9555 1212

dn: cn=boy, dc=sedal,dc=usyd,dc=edu,dc=au


objectclass: person
cn: Boy Milliken
sn: Milliken
userPassword: taboo
telephoneNumber: +61 98989898

© Trendz Information Technologies Ltd. Page No. 92 of 220


J2EE

Make sure that you have installed the correct service provider and your CLASSPATH
variable contains the path to the JNDI packages.

The Code
The code for the white pages service is given in Listing 1. The Java code allows you
to access the LDAP server and search a person or persons by passing a surname.
The code starts by preparing an environment Hashtable object and setting the
necessary properties for the environment.

Hashtable env = new Hashtable();

env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL,
"ldap://sendal.jepit.edu.au:389");

And then, as explained above, you need a DirContext object as the initial context,
which is done by calling the InitialDirContext constructor, passing the environment
Hashtable.

DirContext ctx = new InitialDirContext(env);

Once you have a DirContext object, you can use it to access the LDAP service. To
start searching, use the search method by passing a SearchControls object.

SearchControls constraints = new SearchControls();


constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
NamingEnumeration persons =
ctx.search("dc=sendal,dc=jepit,dc=edu,dc=au",
"(objectclass=person)", constraints);

Then, display the search result, i.e., the attributes of all the person objects that
match the search criteria.

For each person object found, you use the getAttributes method to retrieve the
object's attributes. This method returns the Attributes object. You can then use the
get method of the Attributes object to obtain the value of an attribute by passing the
attribute name.

attributes.get( attributeName );

The part of the code that displays the attribute names of the person objects found is
given below.

System.out.println("Distinguished Name \t| " +


"Common Name \t| Surname \t| Phone");

while (persons != null && persons.hasMore()) {


SearchResult sr = (SearchResult) persons.next();
System.out.print( sr.getName() + "\t| "); // distinguised name
Attributes attrs = sr.getAttributes();
attrs.put(new BasicAttribute("sn", searchedSurname));

© Trendz Information Technologies Ltd. Page No. 93 of 220


J2EE

// attrs.put(new BasicAttribute("cn", "boy"));


System.out.print(attrs.get("cn") + "\t| "); // common name
System.out.print(attrs.get("sn") + "\t| "); // surname
System.out.println(attrs.get("telephoneNumber")); // phone

} // end of while

If you run the code in Listing 1, you can see the result that looks something like the
following.

Distinguished Name | Common Name | Surname | Phone

cn=Boni Milliken |cn: boy |sn: Milliken | +61 9555 1212


cn=Boy Milliken |cn: boy |sn: Milliken | +61 98989898

© Trendz Information Technologies Ltd. Page No. 94 of 220


J2EE

JAVA TRANSACTION API

JTA/XA Transactions
A transaction managed and coordinated by the J2EE platform is a JTA or XA
transaction. A J2EE product is required to support JTA transactions according to the
transaction requirements defined in the J2EE specification. There are two ways to
begin a JTA transaction. A component can begin a JTA transaction explicitly using the
JTA javax.transaction.UserTransaction interface or it can also be started implicitly or
automatically by the EJB container if an EJB bean uses container managed
transaction specification. The main benefit of using JTA transactions is the ability to
seamlessly combine multiple application components and RDBMS/EIS accesses into
one single transaction with a little coding effort. For example, if a component X
begins a JTA transaction and invokes a method of component Y, the transaction will
be propagated transparently from component X to Y by the platform. Enterprise
beans using container-managed transaction demarcation will not need to begin or
commit transactions programmatically as the EJB container itself handles the
demarcation automatically. It is always recommended to access an RDBMS or EIS
within the scope of a JTA transaction. JTA allows applications to access transaction
management independent of any specific implementation by specifying standard Java
interfaces between a transaction manager, the transactional application, the J2EE
server, and the resource managers.

OMG Object Transaction Service


JTS specifies the implementation of a transaction manager that supports JTA and
implements the Java mapping of the OMG Object Transaction Service (OTS) 1.1
specification. JTS propagates transactions using IIOP. A JTS transaction manager
provides the services and management functions required to support transaction
demarcation, transactional resource management, synchronization, and transaction
context propagation. An application component developer uses the JTA
UserTransaction interface to demarcate JTA transaction boundaries in components.
The JTS TransactionManager and XAResource interfaces are low level APIs between a
J2EE server and enterprise information system resource managers and are not
intended to use by applications. A J2EE platform might choose to use a JTS
implementation to support the transaction semantics defined in J2EE specification.
An example is the J2EE SDK. The JTS implementation is transparent to J2EE
components. Components should never interact directly with JTS. Instead, they
should use the JTA UserTransaction interface for transaction demarcation.

Transactions in clients: Case I


This is the case where the J2EE configuration is {Stand-alone Client -> EJB
Container -> RDBMS/EIS Resources}. Transaction support in applets and
application clients is not required by the J2EE platform. However, a J2EE product
might choose to implement and provide this capability for added value. So the ability
of applets and fat standalone application clients to directly access a UserTransaction
object depends entirely on the capabilities provided by the container. To ensure
portability, applets and application clients should delegate transactional work to the
lower tier of enterprise beans.

Transactions in web components: Case II


This is the case where the J2EE configuration is {Browser <--> Web Container <-->
RDBMS/EIS Resources}. It is possible for a servlet or a JSP page to use JNDI lookup
to get hold of a UserTransaction object and use the underlying interface to
programmatically specify transactions. In a two-tier application configuration where a

© Trendz Information Technologies Ltd. Page No. 95 of 220


J2EE

web component needs to access enterprise information systems under the scope of a
JTA transaction, this is quite common. The code snippet below illustrates the use of
the JTA interface to specify transactions within a web component:

Context myCntxt = new InitialContext();


UserTransaction ut =
(UserTransaction) myCntxt.lookup("java:comp/UserTransaction");
ut.begin();
// perform transactional work here
ut.commit();

It is important to keep in mind that a web component like a servlet may only start a
transaction in its service method. Moreover, a transaction started by a servlet or a
JSP page must be completed before the service method returns. Transactions cannot
span across web requests. The following guidelines are recommended for handling
interactions in web components between JTA transactions, threads, and JDBC
connections.

• JTA transactions should start and complete only from the thread in which the
service method is called. Additional threads created in the servlet should not
attempt to start any JTA transaction.
• JDBC connections may be acquired and released by a thread other than the
service method thread, but should not be shared between threads.
• JDBC Connection objects should not be stored in static fields.
• For web components implementing the SingleThreadModel, JDBC Connection
objects may be stored in class instance fields.
• For web components (Servlets) not implementing the SingleThreadModel,
JDBC Connection objects should not be stored in class instance fields and
should be acquired and released within the same invocation of the service
method.

Transactions in application servers: Case III


This is the case where the J2EE configuration is {Browser <-> Web Container <->
EJB Container <-> RDBMS/EIS Resources}. The following scenarios are illustrated to
motivate discussions on use of transactions in application servers with EJB container
and a built in transaction monitor.

Scenario A: Messages sent/received over JMS and multiple databases


access:
It's possible for an application using EJB servers to send messages to or receive
messages from one or more JMS destinations or update data in one or more
databases in a single transaction. In the following figure, a client invokes a method
on the remote interface of enterprise Bean A. which in turn sends a message to a
JMS queue and updates data in a database A. After that, enterprise Bean A calls a
method of another enterprise Bean B that updates data in database B. The
application server with its EJB container and built in transaction manager ensures
that the operations on A, B, and C are either all committed or rolled back.

© Trendz Information Technologies Ltd. Page No. 96 of 220


J2EE

Figure 5. Message sent to JMS queue and updates to multiple


databases

The application programmer does not have to do anything to ensure transactional


semantics. The EJBs A and B perform the sending of the message and database
updates using the standard JMS and JDBC APIs. Behind the scenes, the application
server enlists the session on the connection to the JMS provider and the database
connections as part of the transaction. When the transaction commits, the
application server and the messaging and database systems perform a two-phase
commit protocol to ensure atomic updates across all the three resources.

Scenario B: Multiple database access via multiple Application Servers:


The J2EE architecture allows updates of data at multiple sites to be performed in a
single transaction. In the following figure, a client invokes a method on enterprise
Bean A. which in turn sends a message to a message queue, updates data in
database A. After that, EJB A calls a method in another EJB B deployed in another
application server which updates data in database B. The EJB architecture makes it
possible to perform the updates to databases A and B in a single transaction.

© Trendz Information Technologies Ltd. Page No. 97 of 220


J2EE

Figure 6. Updates to multiple databases in same


transaction

When EJB A invokes EJB B, the two application servers cooperate to propagate the
transaction context from A to B. This transaction context propagation is transparent
to the application. At commit time, the two application servers use distributed two
phase commit protocol (if the capability exists) to ensure the atomicity of the
database updates and the transaction.

The two types of transaction demarcation in enterprise beans, namely, bean


managed and container managed, are discussed in detail next.

Bean managed transaction


As mentioned before, in a bean-managed transaction, an enterprise bean uses the
javax.transaction.UserTransaction interface to explicitly specify transaction
boundaries in the application code. Only session beans and message driven beans
can choose to use bean-managed demarcation. An entity bean must always use
container managed transaction demarcation. The following code illustrates the use of
JTA interface to specify transactions in an enterprise bean with bean-managed
transaction demarcation.

UserTransaction ut = ejbContext.getUserTransaction();
ut.begin();
// Transactional work is done here
ut.commit();

The following example illustrates a business method of a typical session bean that
performs a bean-managed transaction involving both a database connection and a
JMS connection.

public class MySessionEJB implements SessionBean {


EJBContext ejbContext;
public void someMethod(...) {
javax.transaction.UserTransaction ut;

© Trendz Information Technologies Ltd. Page No. 98 of 220


J2EE

javax.sql.DataSource ds;
java.sql.Connection dcon;
java.sql.Statement stmt;
javax.jms.QueueConnectionFactory qcf;
javax.jms.QueueConnection qcon;
javax.jms.Queue q;
javax.jms.QueueSession qsession;
javax.jms.QueueSender qsender;
javax.jms.Message message;
InitialContext initCtx = new InitialContext();
// obtain db conn object and set it up for transactions
ds = (javax.sql.DataSource)
initCtx.lookup("java:comp/env/jdbc/Database");
dcon = ds.getConnection();
stmt = dcon.createStatement();
// obtain jms conn object and set up session for transactions
qcf = (javax.jms.QueueConnectionFactory)
initCtx.lookup("java:comp/env/jms/qConnFactory");
qcon = qcf.createQueueConnection();
qsession = qcon.createQueueSession(true,0);
q = (javax.jms.Queue)
initCtx.lookup("java:comp/env/jms/jmsQueue");
qsender = qsession.createSender(q);
message = qsession.createTextMessage();
message.setText("some message");
//
// Now do a transaction that involves the two connections.
//
ut = ejbContext.getUserTransaction();
// start the transaction
ut.begin();
// Do database updates and send message. The Container
// automatically enlists dcon and qsession with the
// transaction.
stmt.executeQuery(...);
stmt.executeUpdate(...);
stmt.executeUpdate(...);
qsender.send(message);
// commit the transaction
ut.commit();

// release connections
stmt.close();
qsender.close();
qsession.close();
dcon.close();
qcon.close();
}
...
}

© Trendz Information Technologies Ltd. Page No. 99 of 220


J2EE

The following example illustrates a stateful session bean that retains transaction
context across three client calls, invoked in the order {method1, method2, and
method3}.

public class MySessionEJB implements SessionBean {


EJBContext ejbContext;
javax.sql.DataSource ds1;
javax.sql.DataSource ds2;
java.sql.Connection con1;
java.sql.Connection con2;
public void method1(...) {
java.sql.Statement stmt;
InitialContext initCtx = new InitialContext();
// obtain user transaction interface
ut = ejbContext.getUserTransaction();
// start a transaction
ut.begin();
// make some updates on con1
ds1 = (javax.sql.DataSource)
initCtx.lookup("java:comp/env/jdbc/Database1");
con1 = ds1.getConnection();
stmt = con1.createStatement();
stmt.executeUpdate(...);
//
// The Container retains the transaction associated with the
// instance to the next client call (which is method2(...)).
}
public void method2(...) {
java.sql.Statement stmt;
InitialContext initCtx = new InitialContext();
// make some updates on con2
ds2 = (javax.sql.DataSource)
initCtx.lookup("java:comp/env/jdbc/Database2");
con2 = ds2.getConnection();
stmt = con2.createStatement();
stmt.executeUpdate(...);
// The Container retains the transaction associated with the
// instance to the next client call (which is method3(...)).
}
public void method3(...) {
java.sql.Statement stmt;
// obtain user transaction interface
ut = ejbContext.getUserTransaction();
// make some more updates on con1 and con2
stmt = con1.createStatement();
stmt.executeUpdate(...);
stmt = con2.createStatement();
stmt.executeUpdate(...);
// commit the transaction
ut.commit();
// release connections
stmt.close();
con1.close();

© Trendz Information Technologies Ltd. Page No. 100 of 220


J2EE

con2.close();
}
...
}

It is possible for an enterprise bean to open and close a database connection in each
business method rather than hold the connection open until the end of transaction. If
the client in the following example executes the sequence of methods {method1,
method2, method2, method3}, all the database updates done by the multiple
invocations of method2 are performed in the scope of the same transaction. This is
the transaction started in method1 and committed in method3.

public class MySessionEJB implements SessionBean {


EJBContext ejbContext;
InitialContext initCtx;
public void method1(...) {
java.sql.Statement stmt;
// obtain user transaction interface
ut = ejbContext.getUserTransaction();
// start a transaction
ut.begin();
}

public void method2(...) {


javax.sql.DataSource ds;
java.sql.Connection con;
java.sql.Statement stmt;
// open connection
ds = (javax.sql.DataSource)
initCtx.lookup("java:comp/env/jdbc/Database");
con = ds.getConnection();
// make some updates on con
stmt = con.createStatement();
stmt.executeUpdate(...);

// close the connection


stmt.close();
con.close();
}
public void method3(...) {
// obtain user transaction interface
ut = ejbContext.getUserTransaction();
// commit the transaction
ut.commit();
}
...
}

An enterprise bean with bean-managed transaction specification need not and should
not use the getRollbackOnly() and setRollbackOnly()methods of the EJBContext
interface since, if necessary, it can obtain the status of a transaction by using the
getStatus() method of the javax.transaction.UserTransaction interface. It can also
roll back a transaction using the rollback()method of the same interface if required.

© Trendz Information Technologies Ltd. Page No. 101 of 220


J2EE

REMOTE METHOD INVOCATION

Java RMI: Serialization


Serialization is the process of converting a set of object instances that contain
references to each other into a linear stream of bytes, which can then be sent
through a socket, stored to a file, or simply manipulated as a stream of data.
Serialization is the mechanism used by RMI to pass objects between JVMs, either as
arguments in a method invocation from a client to a server or as return values from
a method invocation. In the first section of this book, I referred to this process
several times but delayed a detailed discussion until now. In this chapter, we drill
down on the serialization mechanism; by the end of it, you will understand exactly
how serialization works and how to use it efficiently within your applications.

The Need for Serialization


Envision the banking application while a client is executing a withdrawal. The part of
the application we're looking at has the runtime structure shown in Figure 10-1.

Figure 10-1. Runtime structure when making a


withdrawal

What does it mean for the client to pass an instance of Moneyto the server? At a
minimum, it means that the server is able to call public methods on the instance of
Money.

One way to do this would be to implicitly make Moneyinto a server as well. For
example, imagine that the client sends the following two pieces of information
whenever it passes an instance as an argument:

• The type of the instance; in this case, Money.


• A unique identifier for the object (i.e., a logical reference). For example, the
address of the instance in memory.

The RMI runtime layer in the server can use this information to construct a stub for
the instance of Money, so that whenever the Accountserver calls a method on what it
thinks of as the instance of Money, the method call is relayed over the wire, as
shown in Figure 10-2.

© Trendz Information Technologies Ltd. Page No. 102 of 220


J2EE

Figure 10-2. Relaying a Money method call from the


server

Attempting to do things this way has three significant drawbacks:

• You can't access fields on the objects that have been passed as
arguments.
• Stubs work by implementing an interface. They implement the methods in the
interface by simply relaying the method invocation across the network. That
is, the stub methods take all their arguments and simply marshall them for
transport across the wire. Accessing a public field is really just dereferencing
a pointer--there is no method invocation and hence, there isn't a method call
to forward over the wire.
• It can result in unacceptable performance due to network latency.
Even in our simple case, the instance of Accountis going to need to call
getCents( )on the instance of Money. This means that a simple call to
makeDeposit( )really involves at least two distinct networked method calls:
makeDeposit( )from the client and getCents( )from the server.
• It makes the application much more vulnerable to partial failure.
Let's say that the server is busy and doesn't get around to handling the
request for 30 seconds. If the client crashes in the interim, or if the network
goes down, the server cannot process the request at all. Until all data has
been requested and sent, the application is particularly vulnerable to partial
failures.

This last point is an interesting one. Any time you have an application that requires a
long-lasting and durable connection between client and server, you build in a point of
failure. The longer the connection needs to last, or the higher the communication
bandwidth the connection requires, the more likely the application is to occasionally
break down.

TIP: The original design of the Web, with its stateless connections,
serves as a good example of a distributed application that can tolerate
almost any transient network failure.

These three reasons imply that what is really needed is a way to copy objects and
send them over the wire. That is, instead of turning arguments into implicit servers,
arguments need to be completely copied so that no further network calls are needed
to complete the remote method invocation. Put another way, we want the result of
makeWithdrawal( )to involve creating a copy of the instance of Moneyon the server
side. The runtime structure should resemble Figure 10-3.

© Trendz Information Technologies Ltd. Page No. 103 of 220


J2EE

Figure 10-3. Making a remote method call can create


deep copies of the arguments and return values

The desire to avoid unnecessary network dependencies has two significant


consequences:

• Once an object is duplicated, the two objects are completely


independent of each other.
Any attempt to keep the copy and the original in sync would involve
propagating changes over the network, entirely defeating the reason for
making the copy in the first place.
• The copying mechanism must create deep copies.
If the instance of Moneyreferences another instance, then copies must be
made of both instances. Otherwise, when a method is called on the second
object, the call must be relayed across the wire. Moreover, all the copies must
be made immediately--we can't wait until the second object is accessed to
make the copy because the original might change in the meantime.

These two consequences have a very important third consequence:

• If an object is sent twice, in separate method calls, two copies of the


object will be created.
In addition to arguments to method calls, this holds for objects that are
referenced by the arguments. If you pass object A, which has a reference to
object C, and in another call you pass object B, which also has a reference to
C, you will end up with two distinct copies of C on the receiving side.

Drilling Down on Object Creation


To see why this last point holds, consider a client that executes a withdrawal and
then tries to cancel the transaction by making a deposit for the same amount of
money. That is, the following lines of code are executed:

server.makeWithdrawal(amount);
....
server.makeDeposit(amount);

The client has no way of knowing whether the server still has a copy of amount. After
all, the server may have used it and then thrown the copy away once it was done.
This means that the client has to marshall amountand send it over the wire to the
server.

The RMI runtime can demarshall amount, which is the instance of Money the client
sent. However, even if it has the previous object, it has no way (unless equals()has

© Trendz Information Technologies Ltd. Page No. 104 of 220


J2EE

been overridden) to tell whether the instance it just demarshalled is equal to the
previous object.

More generally, if the object being copied isn't immutable, then the server might
change it. In this case, even if the two objects are currently equal, the RMI runtime
has no way to tell if the two copies will always be equal and can potentially be
replaced by a single copy. To see why, consider our Printer example again. At the end
of Chapter 3, we considered a list of possible feature requests that could be made.
One of them was the following:

Managers will want to track resource consumption. This will involve


logging print requests and, quite possibly, building a set of queries that
can be run against the printer's log.

This can be implemented by adding a few more fields to DocumentDescriptionand


having the server store an indexed log of all the DocumentDescriptionobjects it has
received. For example, we may add the following fields to DocumentDescription:

public Time whenPrinted;


public Person sender;
public boolean printSucceeded;

Now consider what happens when the user actually wants to print two copies of the
same document. The client application could call:

server.printDocument(document);

twice with the "same" instance of DocumentDescription. And it would be an error for
the RMI runtime to create only one instance of DocumentDescriptionon the server
side. Even though the "same" object is passed into the server twice, it is passed as
parts of distinct requests and therefore as different objects.

TIP: This is true even if the runtime can tell that the two instances of
DocumentDescription are equal when it finishes demarshalling. An
implementation of a printer may well have a notion of a job queue that
holds instances of DocumentDescription. So our client makes the first
call, and the copy of document is placed in the queue (say, at number
5), but not edited because the document hasn't been printed yet. Then
our client makes the second call. At this point, the two copies of
document are equal. However, we don't want to place the same object
in the printer queue twice. We want to place distinct copies in the
printer queue.

Thus, we come to the following conclusion: network latency, and the desire to avoid
vulnerability to partial failures, forces us to have a deep copy mechanism for most
arguments to a remote method invocation. This copying mechanism has to make
deep copies, and it cannot perform any validation to eliminate "extra" copies across
methods.

TIP: While this discussion provides examples of implementation


decisions that force two copies to occur, it's important to note that,

© Trendz Information Technologies Ltd. Page No. 105 of 220


J2EE

even without such examples, clients should be written as if the servers


make independent copies. That is, clients are written to use interfaces.
They should not, and cannot, make assumptions about server-side
implementations of the interfaces.

Using Serialization
Serialization is a mechanism built into the core Java libraries for writing a graph of
objects into a stream of data. This stream of data can then be programmatically
manipulated, and reversing the process can make a deep copy of the objects. This
reversal is often called deserialization.

In particular, there are three main uses of serialization:

As a Persistence mechanism
If the stream being used is FileOutputStream, then the data will automatically
be written to a file.

As a Copy mechanism
If the stream being used is ByteArrayOutputStream, then the data will be
written to a byte array in memory. This byte array can then be used to create
duplicates of the original objects.

As a Communication mechanism
If the stream being used comes from a socket, then the data will
automatically be sent over the wire to the receiving socket, at which point
another program will decide what to do.

The important thing to note is that the use of serialization is independent of the
serialization algorithm itself. If we have a serializable class, we can save it to a file or
make a copy of it simply by changing the way we use the output of the serialization
mechanism.

As you might expect, serialization is implemented using a pair of streams. Even


though the code that underlies serialization is quite complex, the way you invoke it is
designed to make serialization as transparent as possible to Java developers. To
serialize an object, create an instance of ObjectOutputStream and call the
writeObject()method; to read in a serialized object, create an instance of
ObjectInputStreamand call the readObject( )object.

ObjectOutputStream
ObjectOutputStream, defined in the java.io package, is a stream that implements the
"writing-out" part of the serialization algorithm. (RMI actually uses a subclass of
ObjectOutputStream to customize its behavior.) The methods implemented by
ObjectOutputStream can be grouped into three categories: methods that write
information to the stream, methods used to control the stream's behavior, and
methods used to customize the serialization algorithm.

The "write()" methods


The first, and most intuitive, category consists of the "write" methods:

© Trendz Information Technologies Ltd. Page No. 106 of 220


J2EE

public void write(byte[] b);


public void write(byte[] b, int off, int len);
public void write(int data);
public void writeBoolean(boolean data);
public void writeByte(int data);
public void writeBytes(String data);
public void writeChar(int data);
public void writeChars(String data);
public void writeDouble(double data);
public void writeFields( );
public void writeFloat(float data);
public void writeInt(int data);
public void writeLong(long data);
public void writeObject(Object obj);
public void writeShort(int data);
public void writeUTF(String s);
public void defaultWriteObject( );

For the most part, these methods should seem familiar. writeFloat( ), for example,
works exactly as you would expect after reading Chapter 1 -- it takes a floating-point
number and encodes the number as four bytes. There are, however, two new
methods here: writeObject() and defaultWriteObject().

The writeObject()serializes an object. In fact, writeObject() is often the instrument of


the serialization mechanism itself. In the simplest and most common case, serializing
an object involves doing two things: creating an ObjectOuptutStreamand calling
writeObject() with a single "top-level" instance. The following code snippet shows the
entire process, storing an object--and all the objects to which it refers--into a file:

FileOutputStream underlyingStream = new FileOutputStream("C:\\temp\\test");


ObjectOutputStream serializer = new ObjectOutputStream(underlyingStream);
serializer.writeObject(serializableObject);

Of course, this works seamlessly with the other methods for writing data. That is, if
you wanted to write two floats, a String, and an object to a file, you could do so with
the following code snippet:

FileOutputStream underlyingStream = new FileOutputStream("C:\\temp\\test");


ObjectOutputStream serializer = new ObjectOutputStream(underlyingStream);
serializer.writeFloat(firstFloat);
serializer.writeFloat(secongFloat);
serializer.writeUTF(aString);
serializer.writeObject(serializableObject);

TIP: ObjectOutputStream's constructor takes an OutputStream as an


argument. This is analogous to many of the streams we looked at in
Chapter 1. ObjectOutputStream and ObjectInputStream are simply
encoding and transformation layers. This enables RMI to send objects
over the wire by opening a socket connection, associating the
OutputStream with the socket connection, creating an
ObjectOutputStream on top of the socket's OutputStream, and then
calling writeObject().

© Trendz Information Technologies Ltd. Page No. 107 of 220


J2EE

The other new "write()" method is defaultWriteObject(). defaultWriteObject( )makes


it much easier to customize how instances of a single class are serialized. However,
defaultWriteObject() has some strange restrictions placed on when it can be called.
Here's what the documentation says about defaultWriteObject( ):

Write the nonstatic and nontransient fields of the current class to this
stream. This may only be called from the writeObject method of the
class being serialized. It will throw the NotActiveException if it is called
otherwise.

That is, defaultWriteObject() is a method that works only when it is called from
another specific method at a particular time. Since defaultWriteObject() is useful only
when you are customizing the information stored for a particular class, this turns out
to be a reasonable restriction. We'll talk more about defaultWriteObject() later in the
chapter, when we discuss how to make a class serializable.

The stream manipulation methods


ObjectOutputStream also implements four methods that deal with the basic
mechanics of manipulating the stream:

public void reset( );


public void close( );
public void flush( );
public void useProtocolVersion(int version);

With the exception of useProtocolVersion( ), these methods should be familiar. In


fact, reset(), close(), and flush()are standard stream methods. useProtocolVersion(),
on the other hand, changes the version of the serialization mechanism that is used.
This is necessary because the serialization format and algorithm may need to change
in a way that's not backwards compatible. If another application needs to read in
your serialized data, and the applications will be versioning independently (or
running in different versions of the JVM), you may want to standardize on a protocol
version.

TIP: There are two versions of the serialization protocol currently


defined: PROTOCOL_VERSION_1 and PROTOCOL_VERSION_2. If you
send serialized data to a 1.1 (or earlier) JVM, you should probably use
PROTOCOL_VERSION_1. The most common case of this involves
applets. Most applets run in browsers over which the developer has no
control. This means, in particular, that the JVM running the applet
could be anything, from Java 1.0.2 through the latest JVM. Most
servers, on the other hand, are written using JDK1.2.2 or later. (The
main exception is EJB containers that require earlier versions of Java.
At this writing, for example, Oracle 8i's EJB container uses JDK 1.1.6.)
If you pass serialized objects between an applet and a server, you
should specify the serialization protocol.

© Trendz Information Technologies Ltd. Page No. 108 of 220


J2EE

Methods that customize the Serialization mechanism


The last group of methods consists mostly of protected methods that provide hooks
that allow the serialization mechanism itself, rather than the data associated to a
particular class, to be customized. These methods are:

public ObjectOutputStream.PutField putFields( );


protected void annotateClass(Class cl);
protected void annotateProxyClass(Class cl);
protected boolean enableReplaceObject(boolean enable);
protected Object replaceObject(Object obj);
protected void drain( );
protected void writeObjectOverride(Object obj);
protected void writeClassDescriptor(ObjectStreamClass classdesc);
protected void writeStreamHeader( );

These methods are more important to people who tailor the serialization algorithm to
a particular use or develop their own implementation of serialization. As such, they
require a deeper understanding of the serialization algorithm. We'll discuss these
methods in more detail later, after we've gone over the actual algorithm used by the
serialization mechanism.

ObjectInputStream
ObjectInputStream, defined in the java.io package, implements the "reading-in" part
of the serialization algorithm. It is the companion to ObjectOutputStream--objects
serialized using ObjectOutputStream can be deserialized using ObjectInputStream.
Like ObjectOutputStream, the methods implemented by ObjectInputStream can be
grouped into three categories: methods that read information from the stream,
methods that are used to control the stream's behavior, and methods that are used
to customize the serialization algorithm.

The "read" methods


The first, and most intuitive, category consists of the "read" methods:

public int read( );


public int read(byte[] b, int off, int len);
public boolean readBoolean( );
public byte readByte( );
public char readChar( );
public double readDouble( );
public float readFloat( );
public intreadInt( );
public long readLong( );
public Object readObject( );
public short readShort( );
public byte readUnsignedByte( );
public short readUnsignedShort( );
public String readUTF( );
void defaultReadObject( );

© Trendz Information Technologies Ltd. Page No. 109 of 220


J2EE

Just as with ObjectOutputStream's write( )methods, these methods should be


familiar. readFloat( ), for example, works exactly as you would expect after reading
Chapter 1: it reads four bytes from the stream and converts them into a single
floating-point number, which is returned by the method call. And, again as with
ObjectOutputStream, there are two new methods here: readObject()and
defaultReadObject( ).

Just as writeObject( ) serializes an object, readObject( ) deserializes it. Deserializing


an object involves doing two things: creating an ObjectInputStreamand then calling
readObject( ). The following code snippet shows the entire process, creating a copy
of an object (and all the objects to which it refers) from a file:

FileInputStream underlyingStream = new FileInputStream("C:\\temp\\test");


ObjectInputStream deserializer = new ObjectInputStream(underlyingStream);
Object deserializedObject = deserializer.readObject( );

This code is exactly inverse to the code we used for serializing the object in the first
place. If we wanted to make a deep copy of a serializable object, we could first
serialize the object and then deserialize it, as in the following code example:

ByteArrayOutputStream memoryOutputStream = new ByteArrayOutputStream( );


ObjectOutputStream serializer = new ObjectOutputStream(memoryOutputStream);
serializer.writeObject(serializableObject);
serializer.flush( );

ByteArrayInputStream memoryInputStream = new


ByteArrayInputStream(memoryOutputStream. toByteArray( ));
ObjectInputStream deserializer = new ObjectInputStream(memoryInputStream);
Object deepCopyOfOriginalObject = deserializer.readObject( );

This code simply places an output stream into memory, serializes the object to the
memory stream, creates an input stream based on the same piece of memory, and
runs the deserializer on the input stream. The end result is a deep copy of the object
with which we started.

The Stream manipulation methods


There are five basic stream manipulation methods defined for ObjectInputStream:

public boolean available( );


public void close( );
public void readFully(byte[] data);
public void readFully(byte[] data, int offset, int size);
public int skipBytes(int len);

Of these, available()and skip()are methods first defined on InputStream. available()


returns a boolean flag indicating whether data is immediately available, and close()
closes the stream.

The three new methods are also straightforward. skipBytes( ) skips the indicated
number of bytes in the stream, blocking until all the information has been read. And
the two readFully( ) methods perform a batch read into a byte array, also blocking
until all the data has been read in.

© Trendz Information Technologies Ltd. Page No. 110 of 220


J2EE

Methods that customize the Serialization mechanism


The last group of methods consists mostly of protected methods that provide hooks,
which allow the serialization mechanism itself, rather than the data associated to a
particular class, to be customized. These methods are:

protected boolean enableResolveObject(boolean enable);


protected Class resolveClass(ObjectStreamClass v);
protected Object resolveObject(Object obj);
protected class resolveProxyClass(String[] interfaces);
protected ObjectStreamClass readClassDescriptor( );
protected Object readObjectOverride( );
protected void readStreamHeader( );
public void registerValidation(ObjectInputValidation obj, int priority);
public GetFields readFields( );

These methods are more important to people who tailor the serialization algorithm to
a particular use or develop their own implementation of serialization. Like before,
they also require a deeper understanding of the serialization algorithm, so I'll hold off
on discussing them right now.

How to Make a Class Serializable


So far, we've focused on the mechanics of serializing an object. We've assumed we
have a serializable object and discussed, from the point of view of client code, how to
serialize it. The next step is discussing how to make a class serializable.

There are four basic things you must do when you are making a class serializable.
They are:

1. Implement the Serializable interface.


2. Make sure that instance-level, locally defined state is serialized properly.
3. Make sure that superclass state is serialized properly.
4. Override equals( ) and hashCode( ).

Let's look at each of these steps in more detail.

Implement the Serializable Interface


This is by far the easiest of the steps. The Serializable interface is an empty
interface; it declares no methods at all. So implementing it amounts to adding,
"implements Serializable" to your class declaration.

Reasonable people may wonder about the utility of an empty interface. Rather than
define an empty interface, and require class definitions to implement it, why not just
simply make every object serializable? The main reason not to do this is that there
are some classes that don't have an obvious serialization. Consider, for example, an
instance of File. An instance of File represents a file. Suppose, for example, it was
created using the following line of code:

File file = new File("c:\\temp\\foo");

© Trendz Information Technologies Ltd. Page No. 111 of 220


J2EE

It's not at all clear what should be written out when this is serialized. The problem is
that the file itself has a different lifecycle than the serialized data. The file might be
edited, or deleted entirely, while the serialized information remains unchanged. Or
the serialized information might be used to restart the application on another
machine, where "C:\\temp\\foo" is the name of an entirely different file.

Another example is provided by the Thread class. A thread represents a flow of


execution within a particular JVM. You would not only have to store the stack, and all
the local variables, but also all the related locks and threads, and restart all the
threads properly when the instance is deserialized.

TIP: Things get worse when you consider platform dependencies. In


general, any class that involves native code is not really a good
candidate for serialization.

Make Sure That Instance-Level, Locally Defined State Is Serialized Properly


Class definitions contain variable declarations. The instance-level, locally defined
variables (e.g., the nonstatic variables) are the ones that contain the state of a
particular instance. For example, in our Money class, we declared one such field:

public class Money extends ValueObject {


private int _cents;
....
}

The serialization mechanism has a nice default behavior -- if all the instance-level,
locally defined variables have values that are either serializable objects or primitive
data types, then the serialization mechanism will work without any further effort on
our part. For example, our implementations of Account, such as Account_Impl, would
present no problems for the default serialization mechanism:

public class Account_Impl extends UnicastRemoteObject implements Account {


private Money _balance;
...
}

While _balance doesn't have a primitive type, it does refer to an instance of Money,
which is a serializable class.

If, however, some of the fields don't have primitive types, and don't refer to
serializable classes, more work may be necessary. Consider, for example, the
implementation of ArrayList from the java.util package. An ArrayList really has only
two pieces of state:

public class ArrayList extends AbstractList implements List, Cloneable, java.io.


Serializable {
private Object elementData[];
private int size;
...
}

© Trendz Information Technologies Ltd. Page No. 112 of 220


J2EE

But hidden in here is a huge problem: ArrayList is a generic container class whose
state is stored as an array of objects. While arrays are first-class objects in Java,
they aren't serializable objects. This means that ArrayList can't just implement the
Serializable interface. It has to provide extra information to help the serialization
mechanism handle its nonserializable fields. There are three basic solutions to this
problem:

• Fields can be declared to be transient.


• The writeObject( )/ readObject( ) methods can be implemented.
• serialPersistentFields can be declared.

Declaring transient fields


The first, and easiest, thing you can do is simply mark some fields using the
transientkeyword. In ArrayList, for example, elementData is really declared to be a
transient field:

public class ArrayList extends AbstractList implements List, Cloneable, java.io.


Serializable {
private transient Object elementData[];
private int size;
...
}

This tells the default serialization mechanism to ignore the variable. In other words,
the serialization mechanism simply skips over the transient variables. In the case of
ArrayList, the default serialization mechanism would attempt to write out size, but
ignore elementData entirely.

This can be useful in two, usually distinct, situations:

The variable isn't serializable


If the variable isn't serializable, then the serialization mechanism will throw an
exception when it tries to serialize the variable. To avoid this, you can declare
the variable to be transient.

The variable is redundant


Suppose that the instance caches the result of a computation. Locally, we
might want to store the result of the computation, in order to save some
processor time. But when we send the object over the wire, we might worry
more about consuming bandwidth and thus discard the cached computation
since we can always regenerate it later on.

Implementing writeObject() and readObject( )


Suppose that the first case applies. A field takes values that aren't serializable. If the
field is still an important part of the state of our instance, such as elementDatain the
case of an ArrayList, simply declaring the variable to be transientisn't good enough.
We need to save and restore the state stored in the variable. This is done by
implementing a pair of methods with the following signatures:

private void writeObject(java.io.ObjectOutputStream out) throws IOException private


void readObject(java.io.ObjectInputStream in) throws IOException,
ClassNotFoundException;

© Trendz Information Technologies Ltd. Page No. 113 of 220


J2EE

When the serialization mechanism starts to write out an object, it will check to see
whether the class implements writeObject(). If so, the serialization mechanism will
not use the default mechanism and will not write out any of the instance variables.
Instead, it will call writeObject() and depend on the method to store out all the
important state. Here is ArrayList's implementation of writeObject():

private synchronized void writeObject(java.io.ObjectOutputStream stream) throws


java.
io.IOException {
stream.defaultWriteObject( );
stream.writeInt(elementData.length);
for (int i=0; i<size; i++)
stream.writeObject(elementData[i]);
}

The first thing this does is call defaultWriteObject(). defaultWriteObject() invokes the
default serialization mechanism, which serializes all the nontransient, nonstatic
instance variables. Next, the method writes out elementData.lengthand then calls the
stream's writeObject( )for each element of elementData.

There's an important point here that is sometimes missed: readObject( )and


writeObject( )are a pair of methods that need to be implemented together. If you do
any customization of serialization inside one of these methods, you need to
implement the other method. If you don't, the serialization algorithm will fail.

Unit Tests and Serialization


Unit tests are used to test a specific piece of functionality in a class. They are
explicitly not end-to-end or application-level tests. It's often a good idea to adopt a
unit-testing harness such as Junit when developing an application. Junit gives you an
automated way to run unit tests on individual classes and is available from
http://www.junit.org/.

If you adopt a unit-testing methodology, then any serializable class should pass the
following three tests:

• If it implements readObject( ), it should implement writeObject( ), and vice-


versa.
• It is equal (using the equals( )method) to a serialized copy of itself.
• It has the same hashcode as a serialized copy of itself.

Similar constraints hold for classes that implement the Externalizable interface.

Declaring serialPersistentFields
The final option that can be used is to explicitly declare which fields should be stored
by the serialization mechanism. This is done using a special static final variable called
serialPersistentFields, as shown in the following code snippet:

private static final ObjectStreamField[] serialPersistentFields = { new

ObjectStreamField("size", Integer.TYPE), .... };

This line of code declares that the field named size, which is of type int, is a serial
persistent field and will be written to the output stream by the serialization

© Trendz Information Technologies Ltd. Page No. 114 of 220


J2EE

mechanism. Declaring serialPersistentFields is almost the opposite of declaring some


fields transient. The meaning of transient is, "This field shouldn't be stored by
serialization," and the meaning of serialPersistentFields is, "These fields should be
stored by serialization."

But there is one important difference between declaring some variables to be


transient and others to be serialPersistentFields. In order to declare variables to be
transient, they must be locally declared. In other words, you must have access to the
code that declares the variable. There is no such requirement for
serialPersistentFields. You simply provide the name of the field and the type.

TIP: What if you try to do both? That is, suppose you declare some
variables to be transient, and then also provide a definition for
serialPersistentFields? The answer is that the transient keyword is
ignored; the definition of serialPersistentFields is definitive.

So far, we've talked only about instance-level state. What about class-level state?
Suppose you have important information stored in a static variable? Static variables
won't get saved by serialization unless you add special code to do so. In our context,
(shipping objects over the wire between clients and servers), static are usually a bad
idea anyway.

Make sure that Superclass state is handled correctly


After you've handled the locally declared state, you may still need to worry about
variables declared in a superclass. If the superclass implements the Serializable
interface, then you don't need to do anything. The serialization mechanism will
handle everything for you, either by using default serialization or by invoking
writeObject( )/ readObject( )if they are declared in the superclass.

If the superclass doesn't implement Serializable, you will need to store its state.
There are two different ways to approach this. You can use serialPersistentFields to
tell the serialization mechanism about some of the superclass instance variables, or
you can use writeObject( )/ readObject( )to handle the superclass state explicitly.
Both of these, unfortunately, require you to know a fair amount about the
superclass. If you're getting the .class files from another source, you should be
aware that versioning issues can cause some really nasty problems. If you subclass a
class, and that class's internal representation of instance-level state changes, you
may not be able to load in your serialized data. While you can sometimes work
around this by using a sufficiently convoluted readObject( ) method, this may not be
a solvable problem. We'll return to this later. However, be aware that the ultimate
solution may be to just implement the Externalizable interface instead, which we'll
talk about later.

Another aspect of handling the state of a nonserializable superclass is that


nonserializable superclasses must have a zero-argument constructor. This isn't
important for serializing out an object, but it's incredibly important when
deserializing an object. Deserialization works by creating an instance of a class and
filling out its fields correctly. During this process, the deserialization algorithm
doesn't actually call any of the serialized class's constructors, but does call the zero-
argument constructor of the first nonserializable superclass. If there isn't a zero-
argument constructor, then the deserialization algorithm can't create instances of the
class, and the whole process fails.

© Trendz Information Technologies Ltd. Page No. 115 of 220


J2EE

WARNING: If you can't create a zero-argument constructor in the


first nonserializable superclass, you'll have to implement the
Externalizable interface instead.

Simply adding a zero-argument constructor might seem a little problematic. Suppose


the object already has several constructors, all of which take arguments. If you
simply add a zero-argument constructor, then the serialization mechanism might
leave the object in a half-initialized, and therefore unusable, state.

However, since serialization will supply the instance variables with correct values
from an active instance immediately after instantiating the object, the only way this
problem could arise is if the constructors actually do something with their
arguments--besides setting variable values.

If all the constructors take arguments and actually execute initialization code as part
of the constructor, then you may need to refractor a bit. The usual solution is to
move the local initialization code into a new method (usually named something like
initialize() ), which is then called from the original constructor:

public MyObject(arglist) {
// set local variables from arglist
// perform local initialization
}
to something that looks like:
private MyObject( ) {
// zero argument constructor, invoked by serialization
// and never by any other
// piece of code.
// note that it doesn't call initialize( )
}

public void MyObject(arglist) {


// set local variables from arglist
initialize( );
}

private void initialize( ) {


// perform local initialization
}

After this is done, writeObject( )/ readObject( ) should be implemented, and


readObject( ) should end with a call to initialize( ). Sometimes this will result in code
that simply invokes the default serialization mechanism, as in the following snippet:

private void writeObject(java.io.ObjectOutputStream stream) throws


java.io.IOException {
stream.defaultWriteObject( );
}

private void readObject(java.io.ObjectInputStream stream) throws


java.io.IOException {
stream.defaultReadObject( );

© Trendz Information Technologies Ltd. Page No. 116 of 220


J2EE

intialize( );
}

TIP: If creating a zero-argument constructor is difficult (for example,


you don't have the source code for the superclass), your class will
need to implement the Externalizable interface instead of Serializable.

Override equals( ) and hashCode( ) if Necessary


The default implementations of equals( ) and hashCode( ), which are inherited from
java.lang.Object, simply use an instance's location in memory. This can be
problematic. Consider our previous deep copy code example:

ByteArrayOutputStream memoryOutputStream = new ByteArrayOutputStream( );


ObjectOutputStream serializer = new ObjectOutputStream(memoryOutputStream);
serializer.writeObject(serializableObject);
serializer.flush( );

ByteArrayInputStream memoryInputStream = new


ByteArrayInputStream(memoryOutputStream.
toByteArray( ));
ObjectInputStream deserializer = new ObjectInputStream(memoryInputStream);
Object deepCopyOfOriginalObject = deserializer.readObject( );

The potential problem here involves the following boolean test:


serializableObject.equals(deepCopyOfOriginalObject)

Sometimes, as in the case of Money and DocumentDescription, the answer should be


true. If two instances of Money have the same values for _cents, then they are
equal. However, the implementation of equals( )inherited from Object will return
false.

The same problem occurs with hashCode(). Note that Object implements hashCode()
by returning the memory address of the instance. Hence, no two instances ever have
the same hashCode( ) using Object's implementation. If two objects are equal,
however, then they should have the same hashcode. So if you need to override
equals( ), you probably need to override hashCode( ) as well.

TIP: With the exception of declaring variables to be transient, all our


changes involve adding functionality. Making a class serializable rarely
involves significant changes to its functionality and shouldn't result in
any changes to method implementations. This means that it's fairly
easy to retrofit serialization onto an existing object hierarchy. The
hardest part is usually implementing equals( ) and hashCode( ).

Making DocumentDescription Serializable


To make this more concrete, we now turn to the DocumentDescriptionclass from the
RMI version of our printer server, which we implemented in Chapter 4. The code for
the first nonserializable version of DocumentDescriptionwas the following:

public class DocumentDescription implements PrinterConstants {


private InputStream _actualDocument;
private int _length;
private int _documentType;

© Trendz Information Technologies Ltd. Page No. 117 of 220


J2EE

private boolean _printTwoSided;


private int _printQuality;

public DocumentDescription(InputStream actualDocument) throws IOException {


this(actualDocument, DEFAULT_DOCUMENT_TYPE, DEFAULT_PRINT_TWO_SIDED,
DEFAULT_PRINT_QUALITY);
}

public DocumentDescription(InputStream actualDocument, int documentType,


boolean
printTwoSided, int printQuality)
throws IOException {
_documentType = documentType;
_printTwoSided = printTwoSided;
_printQuality = printQuality;
BufferedInputStream buffer = new BufferedInputStream(actualDocument);
DataInputStream dataInputStream = new DataInputStream(buffer);
ByteArrayOutputStream temporaryBuffer = new ByteArrayOutputStream( );
_length = copy(dataInputStream, new DataOutputStream(temporaryBuffer));
_actualDocument = new DataInputStream(new
ByteArrayInputStream(temporaryBuffer.toByteArray( )));
}

public int getDocumentType( ) {


return _documentType;
}

public boolean isPrintTwoSided( ) {


return _printTwoSided;
}

public int getPrintQuality( ) {


return _printQuality;
}

private int copy(InputStream source, OutputStream destination) throws


IOException {
int nextByte;
int numberOfBytesCopied = 0;
while(-1!= (nextByte = source.read( ))) {
destination.write(nextByte);
numberOfBytesCopied++;
}
destination.flush( );
return numberOfBytesCopied;
}
}

We will make this into a serializable class by following the steps outlined in the
previous section.

© Trendz Information Technologies Ltd. Page No. 118 of 220


J2EE

Implement the Serializable interface


This is easy. All we need to do is change the class declaration:

public class DocumentDescription implements Serialiazble, PrinterConstants

Make sure that instance-level, locally defined state is serialized properly


We have five fields to take care of:

private InputStream _actualDocument;


private int _length;
private int _documentType;
private boolean _printTwoSided;
private int _printQuality;

Of these, four are primitive types that serialization can handle without any problem.
However, _actualDocumentis a problem. InputStream is not a serializable class. And
the contents of _actualDocumentare very important; _actualDocumentcontains the
document we want to print. There is no point in serializing an instance of
DocumentDescription unless we somehow serialize _actualDocument as well.

If we have fields that serialization cannot handle, and they must be serialized, then
our only option is to implement readObject( ) and writeObject( ). For Document-
Description, we declare _actualDocument to be transient and then implement
readObject( )and writeObject( ) as follows:

private transient InputStream _actualDocument;

private void writeObject(java.io.ObjectOutputStream out) throws IOException {


out.defaultWriteObject( );
copy(_actualDocument, out);
}

private void readObject(java.io.ObjectInputStream in) throws IOException,


ClassNotFoundException {
in.defaultReadObject( );
ByteArrayOutputStream temporaryBuffer = new ByteArrayOutputStream( );
copy(in, temporaryBuffer, _length);
_actualDocument = new DataInputStream(new
ByteArrayInputStream(temporaryBuffer.toByteArray( )));
}
private void copy(InputStream source, OutputStream destination, int length)
throws IOException {
int counter;
int nextByte;
for (counter = 0; counter <length; counter++) {
nextByte = source.read( );
destination.write(nextByte);
}
destination.flush( );
}

© Trendz Information Technologies Ltd. Page No. 119 of 220


J2EE

Note that we declare _actualDocument to be transient and call defaultWriteObject()


in the first line of our writeObject() method. Doing these two things allows the
standard serialization mechanism to serialize the other four instance variables
without any extra effort on our part. We then simply copy _actualDocument to the
stream.

Our implementation of readObject() simply calls defaultReadObject() and then reads


_actualDocument from the stream. In order to read _actualDocument from the
stream, we used the length of the document, which had previously been written to
the stream. In essence, we needed to encode some metadata into the stream, in
order to correctly pull our data out of the stream.

This code is a little ugly. We're using serialization, but we're still forced to think about
how to encode some of our state when we're sending it out of the stream. In fact,
the code for writeObject() and readObject() is remarkably similar to the marshalling
code we implemented directly for the socket-based version of the printer server. This
is, unfortunately, often the case. Serialization's default implementation handles
simple objects very well. But, every now and then, you will want to send a
nonserializable object over the wire, or improve the serialization algorithm for
efficiency. Doing so amounts to writing the same code you write if you implement all
the socket handling yourself, as in our socket-based version of the printer server.

TIP: There is also an order dependency here. The first value written
must be the first value read. Since we start writing by calling
defaultWriteObject( ), we have to start reading by calling default-
ReadObject( ). On the bright side, this means we'll have an accurate
value for _length before we try to read _actualDocument from the
stream.

Make sure that Superclass state is handled correctly


This isn't a problem. The superclass, java.lang.Object, doesn't actually have any
important state that we need to worry about. Since it also already has a zero-
argument constructor, we don't need to do anything.

Override equals() and hashCode( ) if necessary


In our current implementation of the printer server, we don't need to do this. The
server never checks for equality between instances of DocumentDescription. Nor
does it store them in a container object that relies on their hashcodes.

Did We Cheat When Implementing Serializable for DocumentDescription?


It may seem like we cheated a bit in implementing DocumentDescription. Three of
the five steps in making a class serializable didn't actually result in changes to the
code. Indeed, the only work we really did was implementing readObject( ) and
writeObject( ). But it's not really cheating. Serialization is just designed to be easy to
use. It has a good set of defaults, and, at least in the case of value objects intended
to pass over the wire, the default behavior is often good enough.

The Serialization Algorithm


By now, you should have a pretty good feel for how the serialization mechanism
works for individual classes. The next step in explaining serialization is to discuss the
actual serialization algorithm in a little more detail. This discussion won't handle all
the details of serialization (Though we'll come close). Instead, the idea is to cover

© Trendz Information Technologies Ltd. Page No. 120 of 220


J2EE

the algorithm and protocol, so you can understand how the various hooks for
customizing serialization work and how they fit into the context of an RMI
application.

The Data Format


The first step is to discuss what gets written to the stream when an instance is
serialized. Be warned: it's a lot more information than you might guess from the
previous discussion.

An important part of serialization involves writing out class-related metadata


associated with an instance. Most instances are more than one class. For example,
an instance of String is also an instance of Object. Any given instance, however, is an
instance of only a few classes. These classes can be written as a sequence: C1, C2...
CN, in which C1 is a superclass of C2, C2 is a superclass of C3, and so on. This is
actually a linear sequence because Java is a single inheritance language for classes.
We call C1the least superclass and CNthe most-derived class. See Figure 10-4.

Figure 10-4.
Inheritance diagram

After writing out the associated class information, the serialization mechanism stores
out the following information for each instance:

• A description of the most-derived class.


• Data associated with the instance, interpreted as an instance of the least
superclass.
• Data associated with the instance, interpreted as an instance of the second
least superclass.

And so on until:
• Data associated with the instance, interpreted as an instance of the most-
derived class.

So what really happens is that the type of the instance is stored out, and then all the
serializable state is stored in discrete chunks that correspond to the class structure.
But there's a question still remaining: what do we mean by "a description of the
most-derived class?" This is either a reference to a class description that has already
been recorded (e.g., an earlier location in the stream) or the following information:

• The version ID of the class, which is an integer used to validate the. class files
• A boolean stating whether writeObject( )/ readObject( )are implemented

© Trendz Information Technologies Ltd. Page No. 121 of 220


J2EE

• The number of serializable fields


• A description of each field (its name and type)
• Extra data produced by ObjectOutputStream's annotateClass( )method
• A description of its superclass if the superclass is serializable

This should, of course, immediately seem familiar. The class descriptions consist
entirely of metadata that allows the instance to be read back in. In fact, this is one of
the most beautiful aspects of serialization; the serialization mechanism automatically,
at runtime, converts class objects into metadata so instances can be serialized with
the least amount of programmer work.

A Simplified Version of the Serialization Algorithm


In this section, I describe a slightly simplified version of the serialization algorithm. I
then proceed to a more complete description of the serialization process in the next
section.

Writing
Because the class descriptions actually contain the metadata, the basic idea behind
the serialization algorithm is pretty easy to describe. The only tricky part is handling
circular references.

The problem is this: suppose instance A refers to instance B. And instance B refers
back to instance A. Completely writing out A requires you to write out B. But writing
out B requires you to write out A. Because you don't want to get into an infinite loop,
or even write out an instance or a class description more than once you need to keep
track of what's already been written to the stream. (Serialization is a slow process
that uses the reflection API quite heavily in addition to the bandwidth)

ObjectOutputStream does this by maintaining a mapping from instances and classes


to handles. When writeObject( ) is called with an argument that has already been
written to the stream, the handle is written to the stream, and no further operations
are necessary.

If, however, writeObject( ) is passed an instance that has not yet been written to the
stream, two things happen. First, the instance is assigned a reference handle, and
the mapping from instance to reference handle is stored by ObjectOutputStream.
The handle that is assigned is the next integer in a sequence.

TIP: Remember the reset( ) method on ObjectOutputStream? It


clears the mapping and resets the handle counter to 0x7E0000 .RMI
also automatically resets its serialization mechanism after every
remote method call.

Second, the instance data is written out as per the data format described earlier. This
can involve some complications if the instance has a field whose value is also a
serializable instance. In this case, the serialization of the first instance is suspended,
and the second instance is serialized in its place (or, if the second instance has
already been serialized, the reference handle for the second instance is written out).
After the second instance is fully serialized, serialization of the first instance
resumes. The contents of the stream look a little bit like Figure 10-5.

© Trendz Information Technologies Ltd. Page No. 122 of 220


J2EE

Figure 10-5. Contents of Serialization's


data stream.

Reading
From the description of writing, it's pretty easy to guess most of what happens when
readObject() is called. Unfortunately, because of versioning issues, the
implementation of readObject( ) is actually a little bit more complex than you might
guess.

When it reads in an instance description, ObjectInputStream gets the following


information:

• Descriptions of all the classes involved


• The serialization data from the instance

The problem is that the class descriptions that the instance of ObjectInputStream
reads from the stream may not be equivalent to the class descriptions of the same
classes in the local JVM. For example, if an instance is serialized to a file and then
read back in three years later, there's a pretty good chance that the class definitions
used to serialize the instance have changed.

This means that ObjectInputStream uses the class descriptions in two ways:

• It uses them to actually pull data from the stream, since the class
descriptions completely describe the contents of the stream.
• It compares the class descriptions to the classes it has locally and tries to
determine if the classes have changed, in which case it throws an exception.
If the class descriptions match the local classes, it creates the instance and
sets the instance's state appropriately.

RMI Customizes the Serialization Algorithm


RMI doesn't actually use ObjectOutputStream and ObjectInputStream. Instead, it
uses custom subclasses so it can modify the serialization process by overriding some
protected methods. In this section, we'll discuss the most important modifications
that RMI makes when serializing instances. RMI makes similar changes when

© Trendz Information Technologies Ltd. Page No. 123 of 220


J2EE

deserializing instances, but they follow from, and can easily be deduced from, the
description of the serialization changes.

Recall that ObjectOutputStream contained the following protected methods:

protected void annotateClass(Class cl)


protected void annotateProxyClass(Class cl)
protected boolean enableReplaceObject(boolean enable)
protected Object replaceObject(Object obj)
protected void drain( )
protected void writeObjectOverride(Object obj)
protected void writeClassDescriptor(ObjectStreamClass classdesc)
protected void writeStreamHeader( )

These all have default implementations in ObjectOutputStream. That is,


annotateClass() and annotateProxyClass() do nothing. enableReplaceObject() returns
false, and so on. However, these methods are still called during serialization. And
RMI, by overriding these methods, customizes the serialization process.

The three most important methods from the point of view of RMI are:

protected void annotateClass(Class cl)


protected boolean enableReplaceObject(boolean enable)
protected Object replaceObject(Object obj)

Let's describe how RMI overrides each of these.

annotateClass( )
ObjectOutputStream calls annotateClass() when it writes out class descriptions.
Annotations are used to provide extra information about a class that comes from the
serialization mechanism and not from the class itself. The basic serialization
mechanism has no real need for annotations; most of the information about a given
class is already stored in the stream.

TIP: RMI's dynamic classloading system uses annotateClass( ) to


record where .class files are stored. We'll discuss this more in Chapter
19.

RMI, on the other hand, uses annotations to record codebase information. That is,
RMI, in addition to recording the class descriptions, also records information about
the location from which it loaded the class's bytecode. Codebases are often simply
locations in a file system. Incidentally, locations in a file system are often useless
information, since the JVM that deserializes the instances may have a very different
file system than the one from where the instances were serialized. However,
codebase isn't restricted to being a location in a file system. The only restriction on
codebases is that they have to be valid URLs. That is, codebase is a URL that
specifies a location on the network from which the bytecode for a class can be
obtained. This enables RMI to dynamically load new classes based on the serialized
information in the stream.

© Trendz Information Technologies Ltd. Page No. 124 of 220


J2EE

replaceObject( )
The idea of replacement is simple; sometimes the instance that is passed to the
serialization mechanism isn't the instance that ought to be written out to the data
stream. To make this more concrete, recall what happened when we called rebind( )
to register a server with the RMI registry. The following code was used in the bank
example:

Account_Impl newAccount = new Account_Impl(serverDescription.balance);


Naming.rebind(serverDescription.name, newAccount);
System.out.println("Account " + serverDescription.name + " successfully
launched.");
Account_Impl newAccount = new Account_Impl(serverDescription.balance);
Naming.rebind(serverDescription.name, newAccount);
System.out.println("Account " + serverDescription.name + " successfully
launched.");

This creates an instance of Account_Impl and then calls rebind( ) with that instance.
Account_Impl is a server that implements the Remote interface, but not the
Serializable interface. And yet, somehow, the registry, which is running in a different
JVM, is sent something.

What the registry actually gets is a stub. The stub for Account_Impl, which was
automatically generated by rmic, begins with:

public final class Account_Impl_Stub extends java.rmi.server.RemoteStub

java.rmi.server.RemoteStub is a class that implements the Serializable interface. The


RMI serialization mechanism knows that whenever a remote server is "sent" over the
wire, the server object should be replaced by a stub that knows how to communicate
with the server (e.g., a stub that knows on which machine and port the server is
listening).

Calling Naming.rebind( ) actually winds up passing a stub to the RMI registry. When
clients make calls to Naming.lookup( ), as in the following code snippet, they also
receive copies of the stub. Since the stub is serializable, there's no problem in
making a copy of it:

_account = (Account)Naming.lookup(_accountNameField.getText( ));

In order to enable this behavior, ObjectOutputStreamcalls enableReplaceObject( )


and replaceObject( ) during the serialization process. In other words, when an
instance is about to be serialized, ObjectOutputStreamdoes the following:

1. It calls enableReplaceObject( ) to see whether instance replacement is


enabled.
2. If instance replacement is enabled, it calls replaceObject( ), passing in the
instance it was about to serialize, to find out which instance it should really
write to the stream.
3. It then writes the appropriate instance to the stream.

© Trendz Information Technologies Ltd. Page No. 125 of 220


J2EE

Maintaining Direct Connections


A question that frequently arise as distributed applications get more complicated
involves message forwarding. For example, suppose that we have three
communicating programs: A, B, and C. At the start, A has a stub for B, B has a stub
for C, and Chas a stub for A. See Figure 10-6.

Figure 10-6. Communication between


three applications.

Now, what happens if A calls a method, for example, getOtherServer( ), on B that


"returns" C? The answer is that A gets a deep copy of the stub B uses to
communicate with C. That is, A now has a direct connection to C; whenever A tries
to send a message to C, B is not involved at all. This is illustrated in Figure 10-7.

Figure 10-7. Improved


communication between three
applications

This is very good from a bandwidth and network latency point of view. But it can also
be somewhat problematic. Suppose, for example, B implements load balancing.
Since B isn't involved in the A to C communication, it has no direct way of knowing

© Trendz Information Technologies Ltd. Page No. 126 of 220


J2EE

whether A is still using C, or how heavily. We'll revisit this in Chapters and, when we
discuss the distributed garbage collector and the Unreferenced interface.

Versioning Classes
A few pages back, I described the serialization mechanism:

The serialization mechanism automatically, at runtime, converts class


objects into metadata so instances can be serialized with the least
amount of programmer work.

This is great as long as the classes don't change. When classes change, the
metadata, which was created from obsolete class objects, accurately describes the
serialized information. But it might not correspond to the current class
implementations.

The Two Types of Versioning Problems


There are two basic types of versioning problems that can occur. The first occurs
when a change is made to the class hierarchy (e.g., a superclass is added or
removed). Suppose, for example, a personnel application made use of two
serializable classes: Employee and Manager (a subclass of Employee). For the next
version of the application, two more classes need to be added: Contractor and
Consultant. After careful thought, the new hierarchy is based on the abstract
superclass Person, which has two direct subclasses: Employee and Contractor.
Consultant is defined as a subclass of Contractor, and Manager is a subclass of
Employee. See Figure 10-8.

Figure 10-8. Changing the class hierarchy.

While introducing Person is probably good object-oriented design, it breaks


serialization. Recall that serialization relied on the class hierarchy to define the data
format.

The second type of version problem arises from local changes to a serializable class.
Suppose, for example, that in our bank example, we want to add the possibility of
handling different currencies. To do so, we define a new class, Currency, and change
the definition of Money:

public class Money extends ValueObject {


public float amount;
public Currency typeOfMoney;
}
This completely changes the definition of Money but doesn't change the object
hierarchy at all.

The important distinction between the two types of versioning problems is that the
first type can't really be repaired. If you have old data lying around that was

© Trendz Information Technologies Ltd. Page No. 127 of 220


J2EE

serialized using an older class hierarchy, and you need to use that data, your best
option is probably something along the lines of the following:

1. Using the old class definitions, write an application that deserializes the data
into instances and writes the instance data out in a neutral format, say as
tab-delimited columns of text.
2. Using the new class definitions, write a program that reads in the neutral-
format data, creates instances of the new classes, and serializes these new
instances.

The second type of versioning problem, on the other hand, can be handled locally,
within the class definition.

How Serialization detects when a Class has changed


In order for serialization to gracefully detect when a versioning problem has
occurred, it needs to be able to detect when a class has changed. As with all the
other aspects of serialization, there is a default way that serialization does this. And
there is a way for you to override the default.

The default involves a hashcode. Serialization creates a single hashcode, of type


long, from the following information:

• The class name and modifiers


• The names of any interfaces the class implements
• Descriptions of all methods and constructors except private methods and
constructors
• Descriptions of all fields except private, static, and private transient

This single long, called the class's stream unique identifier (often abbreviated suid),
is used to detect when a class changes. It is an extraordinarily sensitive index. For
example, suppose we add the following method to Money:

public boolean isBigBucks( ) {


return _cents > 5000;
}

We haven't changed, added, or removed any fields; we've simply added a method
with no side effects at all. But adding this method changes the suid. Prior to adding
it, the suid was 6625436957363978372L; afterwards, it was
-3144267589449789474L. Moreover, if we had made isBigBucks( ) a protected
method, the suid would have been 4747443272709729176L.

TIP: These numbers can be computed using the serialVer program


that ships with the JDK. For example, these were all computed by
typing serialVer com.ora.rmibook.chapter10.Money at the command
line for slightly different versions of the Money class.

The default behavior for the serialization mechanism is a classic "better safe than
sorry" strategy. The serialization mechanism uses the suid, which defaults to an
extremely sensitive index, to tell when a class has changed. If so, the serialization
mechanism refuses to create instances of the new class using data that was
serialized with the old classes.

© Trendz Information Technologies Ltd. Page No. 128 of 220


J2EE

Implementing Your Own Versioning Scheme


While this is reasonable as a default strategy, it would be painful if serialization didn't
provide a way to override the default behavior. Fortunately, it does. Serialization uses
only the default suid if a class definition doesn't provide one. That is, if a class
definition includes a static final long named serialVersionUID, then serialization will
use that static final long value as the suid. In the case of our Money example, if we
included the line:

private static final long serialVersionUID = 1;

in our source code, then the suid would be 1, no matter how many changes we made
to the rest of the class. Explicitly declaring serialVersionUID allows us to change the
class, and add convenience methods such as isBigBucks( ), without losing backwards
compatibility.

TIP: serialVersionUID doesn't have to be private. However, it must be


static, final, and long.

The downside to using serialVersionUID is that, if a significant change is made (for


example, if a field is added to the class definition), the suid will not reflect this
difference. This means that the deserialization code might not detect an incompatible
version of a class. Again, using Money as an example, suppose we had:

public class Money extends ValueObject {


private static final long serialVersionUID = 1;
protected int _cents;
and we migrated to:
public class Money extends ValueObject {
private static final long serialVersionUID = 1;
public float amount;
public Currency typeOfMoney;
}

The serialization mechanism won't detect that these are completely incompatible
classes. Instead, when it tries to create the new instance, it will throw away all the
data it reads in. Recall that, as part of the metadata, the serialization algorithm
records the name and type of each field. Since it can't find the fields during
deserialization, it simply discards the information.

The solution to this problem is to implement your own versioning inside of


readObject( ) and writeObject( ). The first line in your writeObject( ) method should
begin:

private void writeObject(java.io.ObjectOutputStream out) throws IOException {


stream.writeInt(VERSION_NUMBER);
....
}

In addition, your readObject( ) code should start with a switch statement based on
the version number:

© Trendz Information Technologies Ltd. Page No. 129 of 220


J2EE

private void readObject(java.io.ObjectInputStream in) throws IOException,


ClassNotFoundException {
int version = in.readInt( );
switch(version) {
// version specific demarshalling code.
....}
}private void readObject(java.io.ObjectInputStream in) throws IOException,
ClassNotFoundException {
int version = in.readInt( );
switch(version) {
// version specific demarshalling code.
....}
}

Doing this will enable you to explicitly control the versioning of your class. In addition
to the added control you gain over the serialization process, there is an important
consequence you ought to consider before doing this. As soon as you start to
explicitly version your classes, defaultWriteObject( ) and defaultReadObject( ) lose a
lot of their usefulness.

Trying to control versioning puts you in the position of explicitly writing all the
marshalling and demarshalling code. This is a trade-off you might not want to make.

Performance Issues
Serialization is a generic marshalling and demarshalling algorithm, with many hooks
for customization. As an experienced programmer, you should be skeptical--generic
algorithms with many hooks for customization tends to be slow. Serialization is not
an exception to this rule. It is, at times, both slow and bandwidth-intensive. There
are three main performance problems with serialization: it depends on reflection, it
has an incredibly verbose data format, and it is very easy to send more data than is
required.

Serialization Depends on Reflection


The dependence on reflection is the hardest of these to eliminate. Both serializing
and deserializing require the serialization mechanism to discover information about
the instance it is serializing. At a minimum, the serialization algorithm needs to find
out things such as the value of serialVersionUID, whether writeObject( ) is
implemented, and what the superclass structure is. What's more, using the default
serialization mechanism, (or calling defaultWriteObject( ) from within writeObject( ))
will use reflection to discover all the field values. This can be quite slow.

TIP: Setting serialVersionUID is a simple, and often surprisingly


noticeable, performance improvement. If you don't set
serialVersionUID, the serialization mechanism has to compute it. This
involves going through all the fields and methods and computing a
hash. If you set serialVersionUID, on the other hand, the serialization
mechanism simply looks up a single value.

© Trendz Information Technologies Ltd. Page No. 130 of 220


J2EE

Serialization Has a Verbose Data Format


Serialization's data format has two problems. The first is all the class description
information included in the stream. To send a single instance of Money, we need to
send all of the following:

• The description of the ValueObject class


• The description of the Money class
• The instance data associated with the specific instance of Money.

This isn't a lot of information, but it's information that RMI computes and sends with
every method invocation. (Recall that RMI resets the serialization mechanism with
every method call.) Even if the first two bullets comprise only 100 extra bytes of
information, the cumulative impact is probably significant.

The second problem is that each serialized instance is treated as an individual unit. If
we are sending large numbers of instances within a single method invocation, then
there is a fairly good chance that we could compress the data by noticing
commonalities across the instances being sent.

It is easy to send more data than is required


Serialization is a recursive algorithm. You pass in a single object, and all the objects
that can be reached from that object by following instance variables, are also
serialized. To see why this can cause problems, suppose we have a simple application
that uses the Employee class:

public class Employee implements Serializable {


public String firstName;
public String lastName;
Public String socialSecurityNumber;
}

In a later version of the application, someone adds a new piece of functionality. As


part of doing so, they add a single additional field to Employee:

public class Employee implements Serializable {


public String firstName;
public String lastName;
Public String socialSecurityNumber;
Public Employee manager;
}

What happens as a result of this? On the bright side, the application still works. After
everything is recompiled, the entire application, including the remote method
invocations, will still work. That's the nice aspect of serialization--we added new
fields, and the data format used to send arguments over the wire automatically
adapted to handle our changes. We didn't have to do any work at all.

On the other hand, adding a new field redefined the data format associated with
Employee. Because serialVersionUID wasn't defined in the first version of the class,
none of the old data can be read back in anymore. And there's an even more serious

© Trendz Information Technologies Ltd. Page No. 131 of 220


J2EE

problem: we've just dramatically increased the bandwidth required by remote


method calls.

Suppose Bob works in the mailroom. And we serialize the object associated with Bob.
In the old version of our application, the data for serialization consisted of:

• The class information for Employee


• The instance data for Bob

In the new version, we send:


• The class information for Employee
• The instance data for Bob
• The instance data for Sally (who runs the mailroom and is Bob's manager)
• The instance data for Henry (who is in charge of building facilities)
• The instance data for Alison (Director, Corporate Infrastructure)
• The instance data for Mary (VP in charge of IT)
And so on...

The new version of the application isn't backwards-compatible because our old data
can't be read by the new version of the application. In addition, it's slower and is
much more likely to cause network congestion.

The Externalizable Interface


To solve the performance problems associated with making a class Serializable, the
serialization mechanism allows you to declare that a class is Externalizable instead.
When ObjectOutputStream's writeObject() method is called, it performs the following
sequence of actions:

1. It tests to see if the object is an instance of Externalizable. If so, it uses


externalization to marshall the object.
2. If the object isn't an instance of Externalizable, it tests to see whether the
object is an instance of Serializable. If so, it uses serialization to marshall the
object.
3. If neither of these two cases applies, an exception is thrown.

Externalizableis an interface that consists of two methods:

public void readExternal(ObjectInput in);


public void writeExternal(ObjectOutput out);

These have roughly the same role that readObject() and writeObject( ) have for
serialization. There are, however, some very important differences. The first, and
most obvious, is that readExternal( ) and writeExternal( ) are part of the
Externalizableinterface. An object cannot be declared to be Externalizablewithout
implementing these methods.

However, the major difference lies in how these methods are used. The serialization
mechanism always writes out class descriptions of all the serializable superclasses.
And it always writes out the information associated with the instance when viewed as
an instance of each individual superclasses.

© Trendz Information Technologies Ltd. Page No. 132 of 220


J2EE

Externalization gets rid of some of this. It writes out the identity of the class (which
boils down to the name of the class and the appropriate serialVersionUID). It also
stores the superclass structure and all the information about the class hierarchy. But
instead of visiting each superclass and using that superclass to store some of the
state information, it simply calls writeExternal( ) on the local class definition. In a
nutshell: it stores all the metadata, but writes out only the local instance
information.

TIP: This is true even if the superclass implements Serializable. The


metadata about the class structure will be written to the stream, but
the serialization mechanism will not be invoked. This can be useful if,
for some reason, you want to avoid using serialization with the
superclass. For example, some of the Swing classes, while they claim
to implement Serializable, do so incorrectly (and will throw exceptions
during the serialization process). (JTextAreais one of the most
egregious offenders.) If you really need to use these classes, and you
think serialization would be useful, you may want to think about
creating a subclass and declaring it to be Externalizable. Instances of
your class will be written out and read in using externalization.
Because the superclass is never serialized or deserialized, the incorrect
code is never invoked, and the exceptions are never thrown.

Comparing Externalizable to Serializable


Of course, this efficiency comes at a price. Serializable can be frequently
implemented by doing two things: declaring that a class implements the
Serializableinterface and adding a zero-argument constructor to the class.
Furthermore, as an application evolves, the serialization mechanism automatically
adapts. Because the metadata is automatically extracted from the class definitions,
application programmers often don't have to do anything except recompile the
program.

On the other hand, Externalizable isn't particularly easy to do, isn't very flexible, and
requires you to rewrite your marshalling and demarshalling code whenever you
change your class definitions. However, because it eliminates almost all the reflective
calls used by the serialization mechanism and gives you complete control over the
marshalling and demarshalling algorithms, it can result in dramatic performance
improvements.

To demonstrate this, I have defined the EfficientMoney class. It has the same fields
and functionality as Money but implements Externalizable instead of Serializable:

public class EfficientMoney extends ValueObject implements Externalizable {


public static final long serialVersionUID = 1;
protected int _cents;

public EfficientMoney(Integer cents) {


this(cents.intValue( ));
}

public EfficientMoney(int cents) {


super(cents + " cents.");
_cents = cents;
}

© Trendz Information Technologies Ltd. Page No. 133 of 220


J2EE

public void readExternal(ObjectInput in) throws IOException,


ClassNotFoundException {
_cents = in.readInt( );
_stringifiedRepresentation = _cents + " cents.";
}

public void writeExternal(ObjectOutput out) throws IOException {


out.writeInt(_cents);
}
}

We now want to compare Money with EfficientMoney. We'll do so using the following
application:

public class MoneyWriter {


public static void main(String[] args) {
writeOne( );
writeMany( );
}

private static void writeOne( ) {


try {
System.out.println("Writing one instance");
Money money = new Money(1000);
writeObject("C:\\temp\\foo", money);
}
catch(Exception e){}
}

private static void writeMany( ) {


try {
System.out.println("Writing many instances");
ArrayList listOfMoney = new ArrayList( );
for (int i=0; i<10000; i++) {
Money money = new Money(i*100);
listOfMoney.add(money);
}
writeObject("C:\\temp\\foo2", listOfMoney);
}
catch(Exception e){}
}

private static void writeObject(String filename, Object object) throws


Exception {
FileOutputStream fileOutputStream = new FileOutputStream(filename);
ObjectOutputStream objectOutputStream = new
ObjectOutputStream(fileOutputStream);
long startTime = System.currentTimeMillis( );
objectOutputStream.writeObject(object);
objectOutputStream.flush( );
objectOutputStream.close( );
System.out.println("Time: " + (System.currentTimeMillis( ) - startTime));

© Trendz Information Technologies Ltd. Page No. 134 of 220


J2EE

}
}

On my home machine, averaging over 10 trial runs for both Money and
EfficientMoney, I get the results shown in Table 10-1. (We need to average because
the elapsed time can vary (it depends on what else the computer is doing). The size
of the file is, of course, constant.)

Table 10-1: Testing Money and Efficient Money

Class Number of instances File size Elapsed time

Money 1 266 bytes 60 milliseconds

Money 10,000 309 KB 995 milliseconds

EfficientMoney 1 199 bytes 50 milliseconds

EfficientMoney 10,000 130 KB 907 milliseconds

These results are fairly impressive. By simply converting a leaf class in our hierarchy
to use externalization, I save 67 bytes and 10 milliseconds when serializing a single
instance. In addition, as I pass larger data sets over the wire, I save more and more
bandwidth--on average, 18 bytes per instance.

TIP: Which numbers should we pay attention to? The single-instance


costs or the 10,000-instance costs? For most applications, the single-
instance cost is the most important one. A typical remote method call
involves sending three or four arguments (usually of different types)
and getting back a single return value. Since RMI clears the
serialization mechanism between calls, a typical remote method call
looks a lot more like serializing 3 or 4 single instances than serializing
10,000 instances of the same class.

If I need more efficiency, I can go further and remove ValueObject from the
hierarchy entirely. The ReallyEfficientMoney class directly extends Object and
implements Externalizable:

public class ReallyEfficientMoney implements Externalizable {


public static final long serialVersionUID = 1;
protected int _cents;
protected String _stringifiedRepresentation;

public ReallyEfficientMoney(Integer cents) {


this(cents.intValue( ));
}

public ReallyEfficientMoney(int cents) {


_cents = cents;
_stringifiedRepresentation = _cents + " cents.";
}

© Trendz Information Technologies Ltd. Page No. 135 of 220


J2EE

public void readExternal(ObjectInput in) throws IOException,


ClassNotFoundException {
_cents = in.readInt( );
_stringifiedRepresentation = _cents + " cents.";
}

public void writeExternal(ObjectOutput out) throws IOException {


out.writeInt(_cents);
}
}

ReallyEfficientMoney has much better performance than either Money or


EfficientMoney when a single instance is serialized but is almost identical to
EfficientMoney for large data sets. Again, averaging over 10 iterations, I record the
numbers in Table 10-2.

Table 10-2: Testing ReallyEfficientMoney

Class Number of instances File size Elapsed time

ReallyEfficientMoney 1 74 bytes 20 milliseconds

ReallyEfficientMoney 10,000 127 KB 927 milliseconds

Compared to Money, this is quite impressive; I've shaved almost 200 bytes of
bandwidth and saved 40 milliseconds for the typical remote method call. The
downside is that I've had to abandon my object hierarchy completely to do so; a
significant percentage of the savings resulted from not including ValueObject in the
inheritance chain. Removing superclasses makes code harder to maintain and forces
programmers to implement the same method many times (ReallyEfficientMoney can't
use ValueObject's implementation of equals( ) and hashCode( ) anymore). But it
does lead to significant performance improvements.

© Trendz Information Technologies Ltd. Page No. 136 of 220


J2EE

JAVA MAIL

The design of the Java Mail API is a good example of Sun's continuing efforts to
provide common API frameworks for the Java development community. Emphasizing
these common frameworks, as opposed to vendor-specific solutions, bodes well for
the creation of an increasingly open development environment.

On the e-mail messaging front, higher level (consumer) developers can shop around
for the implementation of the common API framework that best fits their needs -- or
even support multiple implementations simultaneously. Lower level implementation
providers can develop solutions that ensure efficient access to their mail server
products. As an example of what this means, a small startup company can
concentrate on developing that killer mail client and be assured of easily supporting
it for any mail system environment. And the bluechip IT giant can focus on providing
widespread access to its newly developed industrial-strength mail services, assured
of a rich wealth of application support. The big winners are the IS customers, who
can mix and match the best vendor products or solutions to develop their systems
yet still swap components as requirements dictate (whether these be performance,
financial, or political).

One key to developing highly reusable and open API frameworks is to emphasize
abstract interfaces in a way that supports existing standards but does not limit future
enhancements or alternative implementations. The Java Mail API does just that!
Furthermore, Sun is also rapidly developing -- or providing through third parties --
default implementations and utilities for the most commonly available protocols and
standards. For example, default implementations such as POP3, SMTP, and IMAP
protocol servers are currently available, so you can start developing that award-
winning killer app now without having to reinvent the protocol wheel unless you want
to (or really need to).

A close-up looks at the Java Mail API


The layout of packages and classes in the Java Mail API demonstrates one of the
primary goals of its designers -- that the level of effort required by the developer to
build an application should be dictated by the complexity of the application and level
of control required by the developer for the application. In other words, keep the API
as simple as possible. The example application included in this article and the
examples that ship with the Java Mail API amply demonstrate this point.

On first glance, the number of Java Mail API classes and the detailed layout of these
classes may cause you to believe you're in for a heavy learning curve. But in reality,
once you get working, you'll find that this API is a simple and handy tool for
implementing robust mail/messaging functionality in your applications.

Analysis of the primary Java Mail API package classes provides insight into the
common mechanics of e-mail messaging systems. A high-level overview of the
classes in the relative order in which they are normally encountered in a typical
application reveals the simplicity of the Java Mail API.

Although the Java Mail API contains many more classes than those discussed here,
concentrating on some of the core classes to start with makes it easy to understand

© Trendz Information Technologies Ltd. Page No. 137 of 220


J2EE

the essence of the API. The following is a detailed description of these core classes,
which include javax.mail.Session, javax.mail.Store, javax.mail.Transport,
javax.mail.Folder, and javax.mail.Message.

javax.mail.Session
The javax.mail.Session class is the top-level entry class for the Java Mail API, and its
most commonly used methods provide the ability to control and load the classes that
represent the service provider implementations (SPI) for various mail protocols. For
example, instances of the javax.mail.Store and javax.mail.Session classes --
described below -- are obtained via the Session class. (Note: A service provider is a
developer and/or vendor that provides an implementation for an API; examples of
Java Mail API implementations include POP3, SMTP, and IMAP4 -- some are available
from Sun, others via third parties.)

javax.mail.Store
The javax.mail.Store class is implemented by a service provider, such as a POP Mail
implementation developer, and allows for read, write, monitor, and search access for
a particular mail protocol. The javax.mail.Folder class is accessed through this class
and is detailed below.

javax.mail.Transport
The javax.mail.Transport class is another provider-implemented class and is used for
sending a message over a specific protocol.

javax.mail.Folder
The javax.mail.Folder class is implemented by a provider; it gives hierarchical
organization to mail messages and provides access to e-mail messages in the form of
javax.mail.Message class objects.

javax.mail.Message
The javax.mail.Message class is implemented by a provider and models all the details
of an actual e-mail message, such as the subject line, sender/recipient e-mail
address, sent date, and so on. The guidelines for providers who implement the
javax.mail.Message dictate that the actual fetching of e-mail message components
should be delayed as long as possible in order to make this class as lightweight as
possible.

The Java Mail API and JAF


One design fact worth mentioning is that the Java Mail API is tied to, or rather
leverages, another Java extension: the Java Activation Framework (JAF). The JAF is
intended to unify the manner of working with the multitude of data formats that are
available, whether they be simple text or extremely complex documents composed of
images, audio, video, and even "live" objects. In this sense, it might be useful to
think of the JAF as providing for Java what plug-ins provide to a Web browser. (See
this month's article on the Java Plug-in.)

A sample Java Mail application


A "list server application" serves as the focal point for a distributed group discussion
system based on e-mail. As such, it is a very common and useful way for subscribers
to discuss common-interest subjects.

The following Java Mail API-based application is a working example of a useful


middle-tier list server application. The Java Mail API makes this application easy to
create (using the default POP3 and SMTP implementations) and ensures that it will
easily support any provider-implemented systems that should arise. For example, the

© Trendz Information Technologies Ltd. Page No. 138 of 220


J2EE

current application easily supports POP3, SMTP, and IMAP servers, and adding
support for Lotus e-mail, say, would be as simple as plugging in an implementation
from IBM and abstracting any hard coded references to protocols.

Running the ListServer application is very simple. Just remember to include the JAR
files for Java Mail, JAF, and the default POP3 implementation in the CLASSPATH, as
shown in the following MS-DOS batch file example. (You can obtain these JAR files
from the Java Mail home page link provided in the Resources section at the end of
this article.)

@echo off
PATH .;d:\jdk1.1\bin
set CLASSPATH=.;d:\jdk1.1\lib\classes.zip;activation.jar;mail.jar;pop3.jar
java ListServer %1 %2 %3 %4 %5 %6 %7 %8 %9

Upon starting, the ListServer main() routine will read in the settings, including the
appropriate mail servers, mail accounts, and update frequency. Next, an instance of
a ListServer is instantiated and created, and the program enters an infinite loop of
processing new messages and sleeping, until it is time to check for messages again.

The use of java.lang.Properties throughout the application is worth pointing out:


properties are used as a way to implicitly control the behavior of some Java Mail API
methods. It should be noted that sometimes the implicit property arguments could
be overridden by explicit arguments. The disadvantages of allowing implicit property
argument behavior versus explicit argument behavior is (hopefully) more than
balanced by the increased standardization of how applications, and especially
applets, are configured without modifying Java source code.

The heart of this ListServer program occurs in the process() routine, which directs
the reading and broadcasting of all new messages. The significant Java Mail API-
specific code snippets in the method process() perform the following actions:

• Obtains a javax.mail.Session instance, which is used to obtain an instance of


a javax.mail.Store instance
• Uses the javax.mail.Store instance to obtain the default INBOX
javax.mail.Folder instance
• Uses the javax.mail.Folder object to fetch the array of new messages in an
array of javax.mail.Message objects
• Creates a javax.mail.FetchProfile to (potentially) optimize the retrieval of
specific message components

The use of the javax.mail.FetchProfile class is also interesting from a design-and-


implementation perspective. The FetchProfile class provides the option for Java Mail
protocol providers to support more efficient prefetching of Message components. The
design of the Java Mail API encourages the retrieval of Message components to be
delayed as long as possible (that is, until actually used). While this improves
response times, certain operations, such as displaying a list of message subjects,
also benefit from the FetchProfile class. The following code illustrates the use of
FetchProfile when fetching Messages from a Folder:

// Get attributes & flags for all messages


//
Message[] messages = folder.getMessages();

© Trendz Information Technologies Ltd. Page No. 139 of 220


J2EE

FetchProfile fp = new FetchProfile();


fp.add(FetchProfile.Item.ENVELOPE);
fp.add(FetchProfile.Item.FLAGS);
fp.add("X-Mailer");
folder.fetch(messages, fp);

The sendMsg() routine is called by processMsg(), and is responsible for calling


routines to fully read and then broadcast a message. The first step is to set up and
retrieve the default session so that the javax.mail.Transport object that is retrieved
has the correct protocol and host:

// create some properties and get the default Session


//
Properties props = new Properties();
props.put("mail.smtp.host", _smtpHost);
Session session = Session.getDefaultInstance(props, null);
….
….
….
// Send newMessage
//
Transport transport = session.getTransport(SMTP_MAIL);
transport.connect(_smtpHost, _user, _password);
transport.sendMessage(newMessage, _toList);

The code for setting up the message fields such as to, from, subject, and date is very
simple:

// create a message
//
Address replyToList[] = { new InternetAddress(replyTo) };
Message newMessage = new MimeMessage(session);
if (_fromName != null)
newMessage.setFrom(new InternetAddress(from,
_fromName + " on behalf of " + replyTo));
else
newMessage.setFrom(new InternetAddress(from));
newMessage.setReplyTo(replyToList);
newMessage.setRecipients(Message.RecipientType.BCC, _toList);
newMessage.setSubject(subject);
newMessage.setSentDate(sentDate);

Setting the contents of the message requires reading in the desired contents and
then calling the appropriate setContents...() routine as follows:

// Set message contents


//
Object content = message.getContent();
String debugText = "Subject: " + subject + ", Sent date: " + sentDate;
if (content instanceof Multipart)
{
debugMsg("Sending Multipart message (" + debugText + ")");
newMessage.setContent((Multipart)message.getContent());

© Trendz Information Technologies Ltd. Page No. 140 of 220


J2EE

}
else
{
debugMsg("Sending Text message (" + debugText + ")");
newMessage.setText((String)content);
}

The javax.mail.internet.MimeMessage content-reading routines are notable for their


ability to read in complex, multipart, hierarchical messages with one simple call, as
in the getContent() example above.

The source code for ListServer is very basic but provides a fully functional list server.
Furthermore, this basic list server can easily be enhanced by adding features such as
automatic subscribe and unsubscribe.

© Trendz Information Technologies Ltd. Page No. 141 of 220


J2EE

ENTERPRISE JAVA BEANS

Overview
The Enterprise JavaBeans™ (EJB) specification defines an architecture for the
development and deployment of transactional, distributed object applications-based,
server-side software components. Organizations can build their own components or
purchase components from third-party vendors. These server-side components,
called enterprise beans, are distributed objects that are hosted in Enterprise
JavaBean containers and provide remote services for clients distributed throughout
the network.

Enterprise JavaBeans Technology


The Enterprise JavaBeans specification defines an architecture for a transactional,
distributed object system based on components. The specification mandates a
programming model; that is, conventions or protocols and a set of classes and
interfaces, which make up the EJB API. The EJB programming model provides bean
developers and EJB server vendors with a set of contracts that defines a common
platform for development. The goal of these contracts is to ensure portability across
vendors while supporting a rich set of functionality.

The EJB Container


Enterprise beans are software components that run in a special environment called
an EJB container. The container hosts and manages an enterprise bean in the same
manner that the Java Web Server hosts a Servlet or an HTML browser hosts a Java
applet. An enterprise bean cannot function outside of an EJB container. The EJB
container manages every aspect of an enterprise bean at run time including remote
access to the bean, security, persistence, transactions, concurrency, and access to
and pooling of resources.

The container isolates the enterprise bean from direct access by client applications.
When a client application invokes a remote method on an enterprise bean, the
container first intercepts the invocation to ensure persistence, transactions, and
security are applied properly to every operation a client performs on the bean. The
container manages security, transactions, and persistence automatically for the bean,
so the bean developer doesn't have to write this type of logic into the bean code
itself. The enterprise bean developer can focus on encapsulating business rules, while
the container takes care of everything else.

© Trendz Information Technologies Ltd. Page No. 142 of 220


J2EE

Containers will manage many beans simultaneously in the same fashion that the
Java WebServer manages many Servlets. To reduce memory consumption and
processing, containers pool resources and manage the lifecycles of all the beans very
carefully. When a bean is not being used, a container will place it in a pool to be
reused by another client, or possibly evict it from memory and only bring it back
when its needed. Because client applications don't have direct access to the beans --
the container lies between the client and bean -- the client application is completely
unaware of the containers resource management activities. A bean that is not in use,
for example, might be evicted from memory on the server, while its remote reference
on the client remains intact. When the client invokes a method on the remote
reference, the container simply re-incarnates the bean to service the request. The
client application is unaware of the entire process.

An enterprise bean depends on the container for everything it needs. If an enterprise


bean needs to access a JDBC connection or another enterprise bean, it does so
through the container; if an enterprise bean needs to access the identity of its caller,
obtain a reference to itself, or access properties it does so through the container. The
enterprise bean interacts with its container through one of three mechanisms:
callback methods, the EJBContext interface, or JNDI.

• Callback Methods
Every bean implements a subtype of the EnterpriseBean interface which defines
several methods, called callback methods. Each callback method alerts the
bean of a different event in its lifecycle and the container will invoke these
methods to notify the bean when it's about to pool the bean, persist its state
to the database, end a transaction, remove the bean from memory, etc. The
callback methods give the bean a chance to do some housework immediately
before or after some event. Callback methods are discussed in more detail in
later sections.
• EJBContext

© Trendz Information Technologies Ltd. Page No. 143 of 220


J2EE

Every bean obtains an EJBContext object, which is a reference directly to the


container. The EJBContext interface provides methods for interacting with the
container so that that bean can request information about its environment like
the identity of its client, the status of a transaction, or to obtain remote
references to itself.
• Java Naming and Directory Interface
Java Naming and Directory Interface (JNDI) is a standard extension to the
Java platform for accessing naming systems like LDAP, NetWare, file systems,
etc. Every bean automatically has access to a special naming system called
the Environment Naming Context (ENC). The ENC is managed by the
container and accessed by beans using JNDI. The JNDI ENC allows a bean to
access resources like JDBC connections, other enterprise beans, and
properties specific to that bean.

The EJB specification defines a bean-container contract, which includes the


mechanisms (callbacks, EJBContext, JNDI ENC) described above as well as a strict set
of rules that describe how enterprise beans and their containers will behave at
runtime, how security access is checked, how transactions are managed, how
persistence is applied, etc. The bean-container contract is designed to make
enterprise beans portable between EJB containers so that enterprise beans can be
developed once then run in any EJB container. Vendors like BEA, IBM, and GemStone
sell application servers that include EJB containers. Ideally, any enterprise bean that
conforms to the specification should be able to run in any conformant EJB container.

Portability is central to the value that EJB brings to the table. Portability ensures that
a bean developed for one container can be migrated to another if another brand
offers more performance, features, or savings. Portability also means that the bean
developer's skills can be leveraged across several EJB container brands, providing
organizations and developers with better opportunities.

In addition to portability, the simplicity of the EJB programming model makes EJB
valuable. Because the container takes care of managing complex tasks like security,
transactions, persistence, concurrency and resource management the bean
developer is free to focus attention on business rules and a very simple programming
model. A simple programming model means that beans can be developed faster
without requiring a Ph.D. in distributed objects, transactions and other enterprise
systems. EJB brings transaction processing and distributed objects development into
the mainstream.

Enterprise Beans
To create an EJB server-side component, an enterprise bean developer provides two
interfaces that define a bean's business methods, plus the actual bean
implementation class. The client then uses a bean's public interfaces to create,
manipulate, and remove beans from the EJB server. The implementation class, to be
called the bean class, is instantiated at run time and becomes a distributed object.

Enterprise beans live in an EJB container and are accessed by client applications over
the network through their remote and home interfaces. The remote and home
interfaces expose the capabilities of the bean and provide all the method needed to
create, update, interact with, and delete the bean. A bean is a server-side
component that represents a business concept like a Customer or a Hotel Clerk.

© Trendz Information Technologies Ltd. Page No. 144 of 220


J2EE

Remote and Home Interfaces


The Remote and Home interfaces represent the bean, but the container insulates the
beans from direct access from client applications. Every time a bean is requested,
created, or deleted, the container manages the whole process.

The Home interface represents the life-cycle methods of the component (create,
destroy, find) while the remote interface represents the business method of the
bean. The Remote and Home interfaces extend the javax.ejb.EJBObject and
javax.ejb.EJBHome interfaces respectively. These EJB interface types define a standard
set of utility methods and provide common base types for all remote and home
interfaces.

© Trendz Information Technologies Ltd. Page No. 145 of 220


J2EE

Clients use the bean's home interface to obtain references to the bean's remote
interface. The remote interface defines the business methods like accessor and
mutator for changing a Customer's name, or business methods that perform tasks
like using the HotelClerk bean to reserve a room at a hotel. Below is an example of
how a Customer bean might be accessed from a client application. In this case the
home interface is the CustomerHome type and the remote interface is the Customer
type.

CustomerHome home = // ... obtain a reference that


// implements the home interface.

// Use the home interface to create a


// new instance of the Customer bean.
Customer customer = home.create(customerID);

// using a business method on the Customer.


customer.setName(someName);

The remote interface defines the business methods of a bean; the methods that are
specific to the business concept it represents. Remote interfaces are subclassed from
the javax.ejb.EJBObject interface, which is a subclass of the java.rmi.Remote interface.
The importance of the remote interfaces inheritance hierarchy is discussed later. Now

© Trendz Information Technologies Ltd. Page No. 146 of 220


J2EE

focus on the business methods and their meaning. Below is the definition of a remote
interface for a Customer bean.

import javax.ejb.EJBObject;
import java.rmi.RemoteException;

public interface Customer extends EJBObject {

public Name getName() throws RemoteException;


public void setName(Name name) throws RemoteException;
public Address getAddress() throws RemoteException;
public void setAddress(Address address) throws RemoteException;

The remote interface defines accessor and mutator methods to read and update
information about a business concept. This is typical of a type of bean called an
entity bean, which represents a persistent business object; business objects whose
data is stored in a database. Entity beans represent business data in the database
and add behavior specific to that data.

Business Methods
Business methods can also represent tasks that a bean performs. Although entity
beans often have task-oriented methods, tasks are more typical of a type of bean
called a session bean. Session beans do not represent data like entity beans. They
represent business processes or agents that perform a service, like making a
reservation at a hotel. Below is the definition of the remote interface for a HotelClerk
bean, which is a type of session bean.

import javax.ejb.EJBObject;
import java.rmi.RemoteException;

public interface HotelClerk extends EJBObject {

public void reserveRoom(Customer cust, RoomInfo ri,


Date from, Date to)
throws RemoteException;

public RoomInfo availableRooms(Location loc, Date from, Date to)


throws RemoteException;
}

The business methods defined in the HotelClerk remote interface represent processes
rather than simple accessors. The HotelClerk bean acts as an agent in the sense that
it performs tasks on behalf of the user, but is not itself persistent in the database.
You don't need information about the HotelClerk, you need the hotel clerk to perform
tasks for you. This is typical behavior for a session bean.

There are two basic types of enterprise beans: entity beans, which represent data in
a database, and session beans, which represent processes or act as agents
performing tasks. As you build an EJB application you will create many enterprise
beans, each representing a different business concept. Each business concept will be

© Trendz Information Technologies Ltd. Page No. 147 of 220


J2EE

manifested as either an entity bean or a session bean. You will choose which type of
bean a business concept becomes based on how it is intended to be used.

Entity Beans
For every remote interface there is an implementation class; a business object that
actually implements the business methods defined in the remote interface. This is
the bean class; the key element of the bean. Below is a partial definition of the
Customer bean class.

import javax.ejb.EntityBean;

public class CustomerBean implements EntityBean {

Address myAddress;
Name myName;
CreditCard myCreditCard;

public Name getName() {


return myName;
}

public void setName(Name name) {


myName = name;
}

public Address getAddress() {


return myAddress;
}

public void setAddress(Address address) {


myAddress = address;
}

...
}

CustomerBean is the implementation class. It holds the data and provides accessor
methods and other business methods. As an entity bean, the CustomerBean provides
an object view of customer data. Instead of writing database access logic in an
application, the application can simply use the remote interface to the Customer
bean to access customer data. Entity beans implement the javax.ejb.EntityBean type,
which defines a set of notification methods that the bean uses to interact with its
container. These notification methods are examined in detail later in this course.

© Trendz Information Technologies Ltd. Page No. 148 of 220


J2EE

Session Beans
The HotelClerk bean is a session bean, which is similar in many respects to an entity
bean. Session beans represent a set of processes or tasks, which are performed on
behalf of the client application. Session beans may use other beans to perform a task
or access the database directly. A little bit of code shows a session bean doing both.
The reserveRoom() method shown below uses several other beans to a accomplish a
task, while the availableRooms() method uses JDBC to access the database directly.

import javax.ejb.SessionBean;

public class HotelClerkBean implements SessionBean {

public void reserveRoom(Customer cust, RoomInfo ri, Date from, Date to) {
CreditCard card = cust.getCreditCard();
RoomHome roomHome = // ... get home reference
Room room = roomHome.findByPrimaryKey(ri.getID());
double amount = room.getPrice(from,to);
CreditServiceHome creditHome = // ... get home reference
CreditService creditAgent = creditHome.create();
creditAgent.verify(card, amount);
ReservationHome resHome = // ... get home reference
Reservation reservation = resHome.create(cust,room,from,to);
}

public RoomInfo[] availableRooms(Location loc,


Date from, Date to) {
// Make an SQL call to find available rooms
Connection con = // ... get database connection

© Trendz Information Technologies Ltd. Page No. 149 of 220


J2EE

Statement stmt = con.createStatement();


ResultSet results = stmt.executeQuery("SELECT ...");
...
return roomInfoArray;
}
}

You may have noticed that the bean classes defined above do not implement the
remote or home interfaces. EJB doesn't require that the bean class implement these
interfaces; in fact it's discouraged because the base types of the remote and home
interfaces (EJBObject and EJBHome) define a lot of other methods that are
implemented by the container automatically. The bean class does however provide
implementations for all the business methods defined in the remote interface as well
as methods. Callback methods are discussed in more detail below.

Life Cycle Methods


In addition to a remote interface, all beans have a home interface. The home
interface provides life cycle methods for creating, destroying, and locating beans.
These life cycle behaviors are separated out of the remote interface because they
represent behaviors that are not specific to a single bean instance. Below is the
definition of the home interface for the Customer bean. Notice that it extends the
javax.ejb.EJBHome interface, which extends the java.rmi.Remote interface.

import javax.ejb.EJBHome;
import javax.ejb.CreateException;
import javax.ejb.FinderException;
import java.rmi.RemoteException;

public interface CustomerHome extends EJBHome {

public Customer create(Integer customerNumber)


throws RemoteException, CreateException;

public Customer findByPrimaryKey(Integer customerNumber)


throws RemoteException, FinderException;

public Enumeration findByZipCode(int zipCode)


throws RemoteException, FinderException;
}

The create() method is used to create a new entity. This will result in a new record in
the database. A home may have many create() methods. The number and datatype
of the arguments of each create() are left up to the bean developer, but the return
type must be the remote interface datatype. In this case, invoking create() on the
CustomerHome interface will return an instance of Customer. The findByPrimaryKey()
and findByZipCode() methods are used to locate specific instance of the customer
bean. Again, you may define as many find methods as you need.

Back to the Remote and Home Interfaces


The remote and home interfaces are used by applications to access enterprise beans
at run time. The home interface allows the application to create or locate the bean,
while the remote interface allows the application to invoke a bean's business
methods. The below code illustrates:

© Trendz Information Technologies Ltd. Page No. 150 of 220


J2EE

CustomerHome home = // Get a reference to the CustomerHome object

Customer customer = home.create(new Integer(33));

Name name = new Name("Richard", "Wayne", "Monson-Haefel");


customer.setName(name);

Enumeration enumOfCustomers = home.findByZip(55410);

Customer customer2 = home.findByPrimaryKey(new Integer(33));

Name name2 = customer2.getName();

// output is "Richard Wayne Monson-Haefel"


System.out.println(name);

The javax.ejb.EJBHome interface also defines other methods that the CustomerBean
automatically inherits, including a set of remove() methods that allow the application
to destroy bean instances.

Enterprise Beans as Distributed Objects


The remote and home interfaces are types of Java RMI Remote interfaces. The
java.rmi.Remote interface is used by distributed objects to represent the bean in a
different address space (process or machine). An enterprise bean is a distributed
object. That means that the bean class is instantiated and lives in the container but it
can be accessed by applications that live in other address spaces.

To make an object instance in one address space available in another requires a little
trick involving network sockets. To make the trick work, wrap the instance in a
special object called a skeleton that has a network connection to another special
object called a stub. The stub implements the remote interface so it looks like a
business object. But the stub doesn't contain business logic; it holds a network
socket connection to the skeleton. Every time a business method is invoked on the
stub's remote interface, the stub sends a network message to the skeleton telling it
which method was invoked. When the skeleton receives a network message from the
stub, it identifies the method invoked and the arguments, and then invokes the
corresponding method on the actual instance. The instance executes the business
method and returns the result to the skeleton, which sends it to the stub. The
diagram below illustrates:

© Trendz Information Technologies Ltd. Page No. 151 of 220


J2EE

The stub returns the result to the application that invoked its remote interface
method. From the perspective of the application using the stub, it looks like the stub
does the work locally. Actually, the stub is just a dumb network object that sends the
requests across the network to the skeleton, which in turn invokes the method on
the actual instance. The instance does all the work, the stub and skeleton just pass
the method identity and arguments back and forth across the network.

In EJB, the skeleton for the remote and home interfaces is implemented by the
container, not the bean class. This is to ensure that every method invoked on these
reference types by a client application are first handled by the container and then
delegated to the bean instance. The container must intercept these requests
intended for the bean so that it can apply persistence (entity beans), transactions,
and access control automatically.

Distributed object protocols define the format of network messages sent between
address spaces. Distributed object protocols get pretty complicated, but luckily you
don't see any of it because it's handled automatically. Most EJB servers support
either the Java Remote Method Protocol (JRMP) or CORBA's Internet Inter-ORB
Protocol (IIOP). The bean and application programmer only see the bean class and
its remote interface, the details of the network communication are hidden.

With respect to the EJB API, the programmer doesn't care whether the EJB server
uses JRMP or IIOP--the API is the same. The EJB specification requires that you use
a specialized version the Java RMI API, when working with a bean remotely. Java RMI
is an API for accessing distributed objects and is somewhat protocol agnostic -- in
the same way that JDBC is database agnostic. So, an EJB server can support JRMP or
IIOP, but the bean and application developer always uses the same Java RMI API. In
order for the EJB server to have the option of supporting IIOP, a specialized version
of Java RMI, called Java RMI-IIOP was developed. Java RMI-IIOP uses IIOP as the
protocol and the Java RMI API. EJB servers don't have to use IIOP, but they do have
to respect Java RMI-IIOP restrictions, so EJB 1.1 uses the specialized Java RMI-IIOP
conventions and types, but the underlying protocol can be anything.

Entity Type Enterprise Beans


The entity bean is one of two primary bean types: entity and session. The entity
bean is used to represent data in the database. It provides an object-oriented

© Trendz Information Technologies Ltd. Page No. 152 of 220


J2EE

interface to data that would normally be accessed by the JDBC or some other back-
end API. More than that, entity beans provide a component model that allows bean
developers to focus their attention on the business logic of the bean, while the
container takes care of managing persistence, transactions, and access control.

There are two basic kinds of entity bean: Container-Managed Persistence (CMP), and
Bean-Managed Persistence (BMP). With CMP, the container manages the persistence
of the entity bean. Vendor tools are used to map the entity fields to the database and
absolutely no database access code is written in the bean class. With BMP, the entity
bean contains database access code (usually JDBC) and is responsible for reading
and writing its own state to the database. BMP entities have a lot of help with this
since the container will alert the bean as to when it's necessary to make an update
or read its state from the database. The container can also handle any locking or
transactions, so that the database maintains integrity.

Container-Managed Persistence
Container-managed persistence beans are the simplest for the bean developer to
create and the most difficult for the EJB sever to support. This is because all the logic
for synchronizing the bean's state with the database is handled automatically by the
container. This means that the bean developer doesn't need to write any data access
logic, while the EJB server is supposed to take care of all the persistence needs
automatically -- a tall order for any vendor. Most EJB vendors support automatic
persistence to a relational database, but the level of support varies. Some provide
very sophisticated Object-to-Relational mapping, while others are very limited.

In this section, you will expand the CustomerBean developed earlier to a complete
definition of a Container-managed persistence bean. In the next section on Bean-
managed persistence you will modify the CustomerBean to manage its own
persistence.

Bean Class
An enterprise bean is a complete component, which is made up of at least two
interfaces and a bean implementation class. All these types will be presented and
their meaning and application explained, starting with the bean class, which is
defined below:

import javax.ejb.EntityBean;

public class CustomerBean implements EntityBean {

int customerID;
Address myAddress;
Name myName;
CreditCard myCreditCard;

// CREATION METHODS
public Integer ejbCreate(Integer id) {
customerID = id.intValue();
return null;
}

public void ejbPostCreate(Integer id) {


}

© Trendz Information Technologies Ltd. Page No. 153 of 220


J2EE

public Customer ejbCreate(Integer id, Name name) {


myName = name;
return ejbCreate(id);
}

public void ejbPostCreate(Integer id, Name name) {


}

// BUSINESS METHODS
public Name getName() {
return myName;
}

public void setName(Name name) {


myName = name;
}

public Address getAddress() {


return myAddress;
}

public void setAddress(Address address) {


myAddress = address;
}

public CreditCard getCreditCard() {


return myCreditCard;
}

public void setCreditCard(CreditCard card) {


myCreditCard = card;
}

// CALLBACK METHODS
public void setEntityContext(EntityContext cntx) {
}

public void unsetEntityContext() {


}

public void ejbLoad() {


}

public void ejbStore() {


}

public void ejbActivate() {


}

public void ejbPassivate() {


}

© Trendz Information Technologies Ltd. Page No. 154 of 220


J2EE

public void ejbRemove() {


}
}

This is a good example of a fairly simple CMP entity bean. Notice that there is no
database access logic in the bean. This is because the EJB vendor provides tools for
mapping the fields in the CustomerBean to the database. The CustomerBean class, for
example, could be mapped to any database providing it contains data that is similar
to the fields in the bean. In this case the bean's instance fields are comprised of a
primitive int and simple dependent objects (Name, Address,and CreditCard) with their
own attributes. Below are the definitions for these dependent objects.

// The Name class


public class Name implements Serializable {

public String lastName, firstName, middleName;

public Name(String lastName, String firstName,


String middleName) {
this.lastName = lastName;
this.firstName = firstName;
this.middleName = middleName;
}

public Name() {}
}

// The Address class


public class Address implements Serializable {

public String street, city, state, zip;

public Address(String street, String city,


String state, String zip) {
this.street = street;
this.city = city;
this.state = state;
this.zip = zip;
}

public Address() {}
}

// The CreditCard class


public class CreditCard implements Serializable {

public String number, type, name;


public Date expDate;

public CreditCard(String number, String type,


String name, Date expDate) {
this.number = number;
this.type = type;

© Trendz Information Technologies Ltd. Page No. 155 of 220


J2EE

this.name = name;
this.expDate = expDate;
}

public CreditCard() {}
}

These fields are called container-managed fields because the container is responsible
for synchronizing their state with the database; the container manages the fields.
Container-managed fields can be any primitive data types or serializable type. This
case uses both a primitive int (customerID) and serializable objects (Address, Name,
CreditCard). In order to map the dependent objects to the database a fairly
sophisticated mapping tool would be needed. Not all fields in a bean are
automatically container-managed fields; some may be just plain instance fields for
the bean's transient use. A bean developer distinguishes container-managed fields
from plain instance fields by indicating which fields are container-managed in the
deployment descriptor.

The container-managed fields must have corresponding types (columns in RDBMS) in


the database either directly or through Object-Relational mapping. The
CustomerBean might, for example, map to a CUSTOMER table in the database that
has the following definition:

CREATE TABLE CUSTOMER


{
id INTEGER PRIMARY KEY,
last_name CHAR(30),
first_name CHAR(20),
middle_name CHAR(20),
street CHAR(50),
city CHAR(20),
state CHAR(2),
zip CHAR(9),
credit_number CHAR(20),
credit_date DATE,
credit_name CHAR(20),
credit_type CHAR(10)
}

With container-managed persistence, the vendor must have some kind of proprietary
tool that can map the bean's container-managed fields to their corresponding
columns in a specific table, CUSTOMER in this case.

Once the bean's fields are mapped to the database, and the Customer bean is
deployed, the container will manage creating records, loading records, updating
records, and deleting records in the CUSTOMER table in response to methods
invoked on the Customer bean's remote and home interfaces.

A subset (one or more) of the container-managed fields will also be identified as the
bean's primary key. The primary key is the index or pointer to a unique record(s) in
the database that makes up the state of the bean. In the case of the CustomerBean,
the id field is the primary key field and will be used to locate the beans data in the
database. Primitive single field primary keys are represented as their corresponding

© Trendz Information Technologies Ltd. Page No. 156 of 220


J2EE

object wrappers. The primary key of the Customer bean for example is a primitive int
in the bean class but to a bean's clients it will manifest itself as the java.lang.Integer
type. Primary keys that are make up of several fields, called compound primary keys,
will be represented by a special class defined by the bean developer. Primary keys
are similar in concept to primary keys in a relational database -- actually when a
relational database is used for persistence they are often the same thing.

Home Interface
To create a new instance of a CMP entity bean, and therefore insert data into the
database, the create() method on the bean's home interface must be invoked. The
Customer bean's home interface is defined by the CustomerHome interface - the
definition is shown below.

public interface CustomerHome extends javax.ejb.EJBHome {

public Customer create(Integer customerNumber)


throws RemoteException,CreateException;

public Customer create(Integer customerNumber, Name name)


throws RemoteException,CreateException;

public Customer findByPrimaryKey(Integer customerNumber)


throws RemoteException, FinderException;

public Enumeration findByZipCode(int zipCode)


throws RemoteException, FinderException;
}

Below is an example of how the home interface would be used by an application


client to create a new customer:

CustomerHome home = // Get a reference to the CustomerHome object

Name name = new Name("John", "W", "Smith");

Customer customer = home.create(new Integer(33), name);

A bean's home interface may declare zero or more create() methods, each of which
must have corresponding ejbCreate() and ejbPostCreate() methods in the bean class.
These creation methods are linked at run time, so that when a create() method is
invoked on the home interface, the container delegates the invocation to the
corresponding ejbCreate() and ejbPostCreate() methods on the bean class.

When the create() method on a home interface is invoked, the container delegates
the create() method call to the bean instance's matching ejbCreate() method. The
ejbCreate() methods are used to initialize the instance state before a record is
inserted into the database. In this case, they initialize the customerID and Name
fields. When the ejbCreate() method is finished (they return null in CMP) the container
reads the container-managed fields and inserts a new record into the CUSTOMER
table indexed by the primary key, in this case customerID as it maps to the
CUSOTMER.ID column.

© Trendz Information Technologies Ltd. Page No. 157 of 220


J2EE

In EJB, an entity bean doesn't technically exist until after its data has been inserted
into the database, which occurs during the ejbCreate() method. Once the data has
been inserted, the entity bean exists and can access its own primary key and remote
references, which isn't possible until after the ejbCreate() method completes and the
data is in the database. If a bean needs to access its own primary key or remote
reference after its created, but before it services any business methods, it can do so
in the ejbPostCreate() method. The ejbPostCreate() allows the bean to do any post-
create processing before it begins serving client requests. For every ejbCreate() there
must be a matching (matching arguments) ejbPostCreate() method.

The methods in the home interface that begins with "find" are called the find
methods. These are used to query the EJB server for specific entity beans, based on
the name of the method and arguments passed. Unfortunately, there is no standard
query language defined for find methods, so each vendor will implement the find
method differently. In CMP entity beans, the find methods are not implemented with
matching methods in the bean class; containers implement them when the bean is
deployed in a vendor specific manner. The deployer will use vendor specific tools to
tell the container how a particular find method should behave. Some vendors will use
Object-Relational mapping tools to define the behavior of a find method while others
will simply require the deployer to enter the appropriate SQL command.

There are two basic kinds of find methods: single-entity and multi-entity find
methods. Single-entity find methods return a remote reference to the one specific
entity bean that matches the find request. If no entity beans are found, the method
throws an ObjectNotFoundException. Every entity bean must define the single-entity
find method with the method name findByPrimaryKey(), which takes the bean's
primary key type as an argument. (In the above example you used the Integer type
which wrapped the int type of the id field in the bean class.) The multi-entity find
methods return a collection (Enumeration or Collection type) of entities that match the
find request. If no entities are found, the multi-entity find returns an empty
collection. (Note that an empty collection is not the same thing as a null reference.)

Remote Interface
Every entity bean must define a remote interface in addition to the home interface.
The remote interface defines the business methods of the entity bean. The following
is the remote interface definition for the Customer bean:

import javax.ejb.EJBObject;
import java.rmi.RemoteException;

public interface Customer extends EJBObject {

public Name getName()


throws RemoteException;

public void setName(Name name)


throws RemoteException;

public Address getAddress()


throws RemoteException;

public void setAddress(Address address)


throws RemoteException;

© Trendz Information Technologies Ltd. Page No. 158 of 220


J2EE

public CreditCard getCreditCard()


throws RemoteException;

public void setCreditCard(CreditCard card)


throws RemoteException;
}

Below is an example of how a client application would use the remote interface to
interact with a bean:

Customer customer = // ... obtain a remote reference to the bean

// get the customer's address


Address addr = customer.getAddress();

// change the zip code


addr.zip = "56777";

// update the customer's address


customer.setAddress(addr);

The business methods in the remote interface are delegated to the matching
business methods in the bean instance. In the Customer bean, the business methods
are all simple accessors and mutators, but they could be more complicated. In other
words, an entity's business methods are not limited to reading and writing data.
They can also perform tasks and computations.

If customers had, for example, a loyalty program that rewarded frequent use, there
might be methods to upgrade membership in the program based on an accumulation
of overnight stays. See below:

import javax.ejb.EJBObject;
import java.rmi.RemoteException;

public interface Customer extends EJBObject {

public MembershipLevel addNights(int nights_stayed)


throws RemoteException;

public MembershipLevel upgradeMembership()


throws RemoteException;

public MembershipLevel getMembershipLevel()


throws RemoteException;

... Simple accessor business methods go here


}

The addNights() and upgradeMembership() methods are more sophisticated than simple
accessor methods. They apply business rules to change the membership level and go
beyond reading and writing data.

© Trendz Information Technologies Ltd. Page No. 159 of 220


J2EE

Callback Methods
The bean class defines create methods that match methods in the home interface
and business methods that match methods in the remote interface. The bean class
also implements a set of callback methods that allow the container to notify the bean
of events in its lifecycle. The callback methods are defined in the javax.ejb.EntityBean
interface that is implemented by all entity beans, including the CustomerBean class.
The EntityBean interface has the following definition. Notice that the bean class
implements these methods.

public interface javax.ejb.EntityBean {

public void setEntityContext();

public void unsetEntityContext();

public void ejbLoad();

public void ejbStore();

public void ejbActivate();

public void ejbPassivate();

public void ejbRemove();


}

The setEntityContext() method provides the bean with an interface to the container
called the EntityContext. The EntityContext interface contains methods for obtaining
information about the context under which the bean is operating at any particular
moment. The EntityContext interface is used to access security information about the
caller; to determine the status of the current transaction or to force a transaction
rollback; or to get a reference to the bean itself, its home, or its primary key. The
EntityContext is only set once in the life of a entity bean instance, so its reference
should be put into one of the bean instance's fields if it will be needed later.

The Customer bean above doesn't use the EntityContext, but it could. For example, it
could use the EntityContext to validate the caller's membership in a particular security
role. Below is an example, where the EntityContext is used to validate that the caller
is a Manager, the only role (security identity) that can set the credit card type on a
customer to be a WorldWide card, the no-limit card of the super wealthy. (Customers
with this card are automatically tagged for extra service.)

import javax.ejb.EntityBean;

public class CustomerBean implements EntityBean {

int customerID;
Address myAddress;
Name myName;
CreditCard myCreditCard;
EntityContext ejbContext;

© Trendz Information Technologies Ltd. Page No. 160 of 220


J2EE

// CALLBACK METHODS
public void setEntityContext(EntityContext cntx) {
ejbContext = cntx
}

public void unsetEntityContext() {


ejbContext = null;
}

// BUSINESS METHODS
public void setCreditCard(CreditCard card) {
if (card.type.equals("WorldWide"))
if (ejbContext.isCallerInRole("Manager"))
myCreditCard = card;
else
throw new SecurityException();
else
myCreditCard = card;
}

public CreditCard getCreditCard() {


return myCreditCard;
}
...
}

The unsetEntityContext() method is used at the end of the bean's lifecycle -- before
the instance is evicted from memory -- to dereference the EntityContext and perform
any last minuet clean-up.

The ejbLoad() and ejbStore() methods in CMP entities are invoked when the entity
bean's state is being synchronized with the database. The ejbLoad() is invoked just
after the container has refreshed the bean container-managed fields with its state
from the database. The ejbStore() method is invoked just before the container is
about to write the bean container-managed fields to the database. These methods
are used to modify data as its being synchronized. This is common when the data
stored in the database is different than the data used in the bean fields. The
methods might be used, for example, to compress data before it is stored and
decompress it when it is retrieved from the database.

In the Customer bean the ejbLoad() and ejbStore() methods might be used to convert
the dependent objects (Name, Address, CreditCard) to simple String objects and
primitive types, if the EJB container is not sophisticated enough to map the
dependent objects to the CUSTOMER table. Below is an example of how the bean
might be modified.

import javax.ejb.EntityBean;

public class CustomerBean implements EntityBean {

//container-managed fields
int customerID;
String lastName;

© Trendz Information Technologies Ltd. Page No. 161 of 220


J2EE

String firstName;
String middleName;
...

// not-container-managed fields
Name myName;
Address myAddress;
CreditCard myCreditCard;

// BUSINESS METHODS
public Name getName() {
return myName;
}

public void setName(Name name) {


myName = name;
}
...

public void ejbLoad() {

if (myName == null)
myName = new Name();

myName.lastName = lastName;
myName.firstName = firstName;
myName.middleName = middleName;
...
}

public void ejbStore() {

lastName = myName.lastName;
firstName = myName.firstName;
middleName = myName.middleName;
...
}
}

The ejbPassivate() and ejbActivate() methods are invoked on the bean by the container
just before the bean is passivated and just after the bean is activated, respectively.
Passivation in entity beans means that the bean instance is disassociated with its
remote reference so that the container can evict it from memory or reuse it. It's a
resource conservation measure the container employs to reduce the number of
instances in memory. A bean might be passivated if it hasn't been used for a while or
as a normal operation performed by the container to maximize reuse of resources.
Some containers will evict beans from memory, while others will reuse instances for
other more active remote references. The ejbPassivate() and ejbActivate() methods
provide the bean with a notification as to when its about to be passivated
(disassociated with the remote reference) or activated (associated with a remote
reference).

© Trendz Information Technologies Ltd. Page No. 162 of 220


J2EE

Bean-Managed Persistence
The bean-managed persistence (BMP) enterprise bean manages synchronizing its
state with the database as directed by the container. The bean uses a database API
(usually JDBC) to read and write its fields to the database, but the container tells it
when to do each synchronization operation and manages the transactions for the
bean automatically. Bean-managed persistence gives the bean developer the
flexibility to perform persistence operations that are too complicated for the
container or to use a data source that is not supported by the container -- custom
and legacy databases for example.

In this section, you'll modify the CustomerBean class to be a BMP bean instead of a
CMP bean. This modification will not impact the remote or home interface at all. In
fact, you won't modify the original CustomerBean directly. Instead, you'll change it to
bean-managed persistence by extending the bean and overriding the appropriate
methods. Below is the definition of the class that will extend the Customer bean class
to make it a BMP entity. In most cases, you will not extend a bean to make it BMP,
you will just implement the bean as BMP directly. This strategy (extending the CMP
bean) is done for two reasons: it allows the bean to be either a CMP or BMP bean;
and it conveniently cuts down on the amount of code needed to display. Below is the
definition of the BMP class which will be added to as this section proceeds:

public class CustomerBean_BMP extends CustomerBean {


public void ejbLoad() {
// override implementation
}
public void ejbStore() {
// override implementation
}
public void ejbCreate() {
// override implementation
}
public void ejbRemove() {
// override implementation
}
private Connection getConnection() {
// new helper method
}
}

With BMP beans, the ejbLoad() and ejbStore() methods are used differently by the
container and bean than was the case in CMP. In BMP, the ejbLoad() and ejbStore()
methods contain code to read the bean's data from the database and to write
changes to the database, respectively. These methods are called on the bean
automatically, when the EJB server decides it's a good time to read or write data.

The CustomerBean_BMP bean manages its own persistence. In other words, the
ejbLoad() and ejbStore() methods must include database access logic, so that the
bean can load and store its data when the EJB container tells it to. The container will
execute the ejbLoad() and ejbStore() methods automatically when appropriate.

© Trendz Information Technologies Ltd. Page No. 163 of 220


J2EE

The ejbLoad() method is usually invoked by the container at the beginning of a


transaction, just before the container delegates a business method to the bean. The
code below shows how to implement the ejbLoad() method using JDBC.

import java.sql.Connection;

public class CustomerBean_BMP extends CustomerBean {

public void ejbLoad() {


Connection con;
try {
Integer primaryKey = (Integer)ejbContext.getPrimaryKey();
con = this.getConnection();
Statement sqlStmt =
con.createStatement("SELECT * FROM Customer " +
" WHERE customerID = " +
primaryKey.intValue());
ResultSet results = sqlStmt.executeQuery();
if (results.next()) {
// get the name information from the customer table
myName = new Name();
myName.first = results.getString("FIRST_NAME");
myName.last = results.getString("LAST_NAME");
myName.middle = results.getString("MIDDLE_NAME");
// get the address information from the customer table
myAddress = new Address();
myAddress.street = results.getString("STREET");
myAddress.city = results.getString("CITY");
myAddress.state = results.getString("STATE");
myAddress.zip = results.getInt("ZIP");
// get the credit card information from the customer table
myCreditCard = new CreditCard();
myCreditCard.number = results.getString("CREDIT_NUMBER");
myCreditCard.expDate = results.getString("CREDIT_DATE");
myCreditCard.type = results.getString("CREDIT_TYPE");
myAddress.name = results.getInt("CREDIT_NAME");
}
}
catch (SQLException sqle) {
throw new EJBException(sqle);
}
finally {
if (con!=null)
con.close();
}
}
}

In the ejbLoad() method, use the ejbContext() reference to the bean's EntityContext to
obtain the instance's primary key. This ensures that you use the correct index to the
database. Obviously, the CustomerBean_BMP will need to use the inherited
setEntityContext() and unsetEntityContext() methods as illustrated earlier.

© Trendz Information Technologies Ltd. Page No. 164 of 220


J2EE

The ejbStore() method is invoked by the container on the bean, at the end of a
transaction, just before the container attempts to commit all changes to the
database.

import java.sql.Connection;

public class CustomerBean_BMP extends CustomerBean {

public void ejbLoad() {


... read data from database
}

public void ejbStore() {


Connection con;
try {
Integer primaryKey = (Integer)ejbContext.getPrimaryKey();
con = this.getConnection();
PreparedStatement sqlPrep =
con.prepareStatement(
"UPDATE customer set " +
"last_name = ?, first_name = ?, middle_name = ?, " +
"street = ?, city = ?, state = ?, zip = ?, " +
"card_number = ?, card_date = ?, " +
"card_name = ?, card_name = ?, " +
"WHERE id = ?"
);
sqlPrep.setString(1,myName.last);
sqlPrep.setString(2,myName.first);
sqlPrep.setString(3,myName.middle);
sqlPrep.setString(4,myAddress.street);
sqlPrep.setString(5,myAddress.city);
sqlPrep.setString(6,myAddress.state);
sqlPrep.setString(7,myAddress.zip);
sqlPrep.setInt(8, myCreditCard.number);
sqlPrep.setString(9, myCreditCard.expDate);
sqlPrep.setString(10, myCreditCard.type);
sqlPrep.setString(11, myCreditCard.name);
sqlPrep.setInt(12,primaryKey.intValue());
sqlPrep.executeUpdate();
}
catch (SQLException sqle) {
throw new EJBException(sqle);
}
finally {
if (con!=null)
con.close();
}
}
}

In both the ejbLoad() and ejbStore() methods the bean synchronizes its own state with
the database using JDBC. If you studied the code carefully you may have noticed
that the bean obtains its database connection from the mysterious this.getConnection()

© Trendz Information Technologies Ltd. Page No. 165 of 220


J2EE

method invocation, a method yet to be implemented. The getConnection() method is


not a standard EJB method, its just a private helper method implemented to conceal
the mechanics of obtaining a database connection. Below is the definition of the
getConnection() method.

import java.sql.Connection;

public class CustomerBean_BMP extends CustomerBean {

public void ejbLoad() {


... read data from database
}

public void ejbStore() {


... write data to database
}

private Connection getConnection() throws SQLException {

InitialContext jndiContext = new InitialContext();


DataSource source = (DataSource)
jndiContext.lookup("java:comp/env/jdbc/myDatabase");
return source.getConnection();
}
}

Database connections are obtained from the container using a default JNDI context
called the JNDI Environment Naming Context (ENC). The ENC provides access to
transactional, pooled, JDBC connections through the standard connection factory, the
javax.sql.DataSource type.

In BMP, the ejbLoad() and ejbStore() methods are invoked by the container to
synchronize the bean instance with data in the database. To insert and remove
entities in the database, the ejbCreate() and ejbRemove() methods are implemented
with similar database access logic. The ejbCreate() methods are implemented so that
a new record is inserted into the database and the ejbRemove() methods are
implemented so that the entity's data is deleted from the database. The ejbCreate()
methods and the ejbRemove() method of a BMP entity are invoked by the container in
response to invocations on their corresponding methods in the home and remote
interfaces. The implementations of these methods are shown below.

public void ejbCreate(Integer id) {


this.customerID = id.intValue();
Connection con;
try {
con = this.getConnection();
Statement sqlStmt =
con.createStatement("INSERT INTO customer id VALUES (" +
customerID + ")");
sqlStmt.executeUpdate();
return id;
}
catch(SQLException sqle) {

© Trendz Information Technologies Ltd. Page No. 166 of 220


J2EE

throw new EJBException(sqle);


}
finally {
if (con!=null)
con.close();
}
}

public void ejbRemove() {


Integer primaryKey = (Integer)ejbContext.getPrimaryKey();
Connection con;
try {
con = this.getConnection();
Statement sqlStmt =
con.createStatement("DELETE FROM customer WHERE id = "
primaryKey.intValue());
sqlStmt.executeUpdate();
}
catch(SQLException sqle) {
throw new EJBException(sqle);
}
finally {
if (con!=null)
con.close();
}
}

In BMP, the bean class is responsible for implementing the find methods defined in
the home interface. For each find method defined in the home interface there must
be corresponding ejbFind() method in the bean class. The ejbFind() methods locate the
appropriate bean records in the database and return their primary keys to the
container. The container converts the primary keys into bean references and returns
them to the client. Below is an example implementation of the ejbFindByPrimaryKey()
method in the CustomerBean_BMP class, which corresponds to the findByPrimaryKey()
defined in the CustomerHome interface.

public Integer ejbFindByPrimaryKey(Integer primaryKey)


throws ObjectNotFoundException {
Connection con;
try {
con = this.getConnection();
Statement sqlStmt =
con.createStatement("SELECT * FROM Customer " +
" WHERE customerID = " +
primaryKey.intValue());

ResultSet results = sqlStmt.executeQuery();


if (results.next())
return primaryKey;
else
throw ObjectNotFoundException();
}
catch (SQLException sqle) {

© Trendz Information Technologies Ltd. Page No. 167 of 220


J2EE

throw new EJBException(sqle);


}
finally {
if (con!=null)
con.close();
}
}

Single-entity find methods like the one above return a single primary key or throw
the ObjectNotFoundException if no matching record is located. Multi-entity find
methods return a collection (java.util.Enumeration or java.util.Collection) of primary
keys. The container converts the collection of primary keys into a collection of
remote references, which are returned to the client. Below is an example of how the
multi-entity ejbFindByZipCode() method, which corresponds to the findByZipCode()
method defined in the CustomerHome interface, would be implemented in the
CustomerBean_BMP class.

public Enumeration ejbFindByZipCode(int zipCode) {


Connection con;
try {
con = this.getConnection();
Statement sqlStmt =
con.createStatement("SELECT id FROM Customer " +
" WHERE zip = " +zipCode);

ResultSet results = sqlStmt.executeQuery();


Vector keys = new Vector();
while(results.next()){
int id = results.getInt("id");
keys.addElement(new Integer(id));
}
return keys.elements();
}
catch (SQLException sqle) {
throw new EJBException(sqle);
}
finally {
if (con!=null)
con.close();
}
}

If no matching bean records are found, an empty collection is returned to the


container, which returns an empty collection to the client.

With the implementation of all these methods and a few minor changes to the bean's
deployment descriptor, the CustomerBean_BMP is ready to be deployed as a BMP
entity.

Session Type Enterprise Beans


Session beans are used to manage the interactions of entity and other session
beans, access resources, and generally perform tasks on behalf of the client. Session

© Trendz Information Technologies Ltd. Page No. 168 of 220


J2EE

beans correspond to the controller in a model-view-controller architecture because


they encapsulate the business logic of a 3-tier architecture.

There are two basic kinds of session bean: Stateless and Stateful. Stateless session
beans are made up of business methods that behave like procedures; they operate
only on the arguments passed to them when they are invoked. Stateless beans are
called "stateless" because they are transient; they do not maintain business state
between method invocations. Stateful session beans encapsulate business logic and
state specific to a client. Stateful beans are called "stateful" because they do
maintain business state between method invocations, held in memory and not
persistent.

Stateless Session Beans


Stateless session beans, like all session beans, are not persistent business objects as
are entity beans. They do not represent data in the database. Instead, they
represent business processes or tasks that are performed on behalf of the client
using them. The business methods in a stateless bean act more like traditional
procedures in a legacy transaction-processing monitor. Each invocation of a stateless
business method is independent from previous invocations. Because stateless session
beans are "stateless" they are easier for the EJB container to manage, so they tend
to process requests faster and use less resources. But this performance advantage
comes at a price; stateless session beans are stupid, they don't remember anything
from one method invocation to the next.

An example of a stateless session bean is a CreditService bean, representing a credit


service that can validate and process credit card charges. A Hotel chain might
develop a CreditService bean to encapsulate the process of verifying a credit card
number, making a charge, and recording the charge in the database for accounting
purposes. Below are the remote and home interfaces for the CreditService bean.

// remote interface
public interface CreditService extends javax.ejb.EJBObject {
public void verify(CreditCard card, double amount)
throws RemoteException, CreditServiceException;
public void charge(CreditCard card, double amount)
throws RemoteException, CreditServiceException;
}

// home interface
public interface CreditServiceHome extends java.ejb.EJBHome {
public CreditService create()
throws RemoteException, CreateException;
}

The remote interface, CreditService, defines two methods, verify() and charge() which
are used by the Hotel to verify and charge credit cards. The Hotel might use the
verify() method to make a reservation, but not charge the customer. The charge()
method would be used to charge a customer for a room. The home interface,
CreditServiceHome provides one create() method with no arguments. All home
interfaces for stateless session beans will define just one method, a no-argument
create() method, because session beans do not have find methods and they cannot
be initiated with any arguments when they are created. Stateless session beans do
not have find methods, because stateless beans are all equivalent and are not

© Trendz Information Technologies Ltd. Page No. 169 of 220


J2EE

persistent. In other words, there is no unique stateless session beans that can be
located in the database. Because stateless session beans are not persisted, they are
transient services. Every client that uses the same type of session bean gets the
same service.

Below is the bean class definition for the CreditService bean. This bean encapsulates
access to the Acme Credit Card processing services. Specifically, this bean accesses
the Acme secure web server and posts requests to validate or charge the customer's
credit card.

import javax.ejb.SessionBean;

public class CreditServiceBean implements SessionBean {


URL acmeURL;
HttpURLConnection acmeCon;

public void ejbCreate() {


try {
InitialContext jndiContext = new InitialContext();
URL acmeURL = (URL)
jndiContext.lookup("java:comp/ejb/env/url/acme");
acmeCon = acmeURL.openConnection();
}
catch (Exception e) {
throws new EJBException(e);
}
}

public void verify(CreditCard card, double amount) {


String response = post("verify:" + card.postString() +
":" + amount);
if (response.substring("approved")== -1)
throw new CreditServiceException("denied");
}

public void charge(CreditCard card, double amount)


throws CreditCardException {
String response = post("charge:" + card.postString() +
":" + amount);
if (response.substring("approved")== -1)
throw new CreditServiceException("denied");
}

private String post(String request) {


try {
acmeCon.connect();
acmeCon.setRequestMethod("POST "+request);
String response = acmeCon.getResponseMessage();
}
catch (IOException ioe) {
throw new EJBException(ioe);
}
}

© Trendz Information Technologies Ltd. Page No. 170 of 220


J2EE

public void ejbRemove() {


acmeCon.disconnect();
}

public void setSessionContext(SessionContext cntx) {}

public void ejbActivate() {}

public void ejbPassivate() {}


}

The CreditService stateless bean demonstrates that a stateless bean can represent a
collection of independent but related services. In this case credit card validation and
charges are related but not necessarily interdependent. Stateless beans might be
used to access unusual resources -- as is the case with the CreditService bean --
databases or to perform complex computations. The CreditService bean is used as an
example in this tutorial to demonstrate the service nature of a stateless bean and to
provide a context for discussing the behavior of the call back methods. The use of a
stateless session beans is not limited to the behavior illustrated in this example;
stateless beans can be used to perform any kind of service.

The CreditServiceBean class uses a URL resource factory (acmeURL()) to obtain and
maintain a reference to the Acme web server which exists on another computer very
far away. The CreditServiceBean uses the acmeURL() to obtain a connection to the
web server and post requests for the validation and charge of credit cards. The
CreditService bean is used by clients instead of a direct connection so that the
service can be better managed by the EJB container, which will pool connections and
managed transactions and security automatically for the EJB client.

The ejbCreate() method is invoked at the beginning of its lifetime and is invoked only
once. The ejbCreate() method is a convenient method for initiating resource
connections and variables that will be of used to the stateless bean for its lifetime. In
the example above, the CreditServiceBean uses the ejbCreate() to obtain a reference to
the HttpURLConnection factory, which it will use throughout its lifetime to obtain
connections to the Acme web server.

The CreditServiceBean uses the JNDI ENC to obtain a URL connection factory in the
same way that the CustomerBean used the JNDI ENC to obtain a DataSource resource
factory for JDBC connections. The JNDI ENC is a default JNDI context that all beans
have access to automatically. The JNDI ENC is used to access static properties, other
beans, and resource factories like the java.net.URL and JDBC javax.sql.DataSource. In
addition, the JNDI ENC also provides access to JavaMail and Java Messaging Service
resource factories.

The ejbCreate() and ejbRemove() methods are each only invoked once in its lifetime by
the container; when the bean is first created and when it's finally destroyed.
Invocations of the create() and remove() methods on its home and remote interfaces
by the client do not result in invocations on the ejbCreate() and ejbRemove() methods
on the bean instance. Instead, an invocation of the create() method provides the
client with a reference to the stateless bean type and the remove() methods
invalidate the reference. The container will decide when bean instances are actually
created and destroyed and will invoke the ejbCreate() and ejbRemove() methods at

© Trendz Information Technologies Ltd. Page No. 171 of 220


J2EE

these times. This allows stateless instances to be shared between many clients
without impacting the clients' references.

In the CreditServiceBean, the ejbCreate() and ejbRemove() methods are used to obtain a
URL connection at the beginning the bean instance's life and to disconnect from it at
the end of the bean instance's life. Between the times that the URL connection is
obtained and disconnected it is used by the business methods. This approach is used
to reduce the number of times that a connection needs to be obtained and
disconnected, conserving resources. It may seem wasteful to maintain the URL
connection between method invocations, but stateless session beans are designed to
be shared between many clients, so that they are in constant use. As soon as a
stateless instance completes a method invocation for a client, it can immediately
service another client. Ideally, there is little down time for a stateless session bean
instance, so it makes sense to maintain the URL connection.

The verify() and charge() methods delegate their requests to the post() method, a
private helper method. The post() method uses an HttpURLConnection to submit the
credit card information to the Acme web server and return the reply to the verify() or
charge() method. The HttpURLConnection may have been disconnected automatically
by the container -- this might occur if, for example, a lot of time elapsed since its last
use -- so the post() method always invokes the connect() method, which does nothing
if the connection is already established. The verify() and charge() methods parse the
return value looking for the substring "approved" which indicates that the credit card
was not denied. If "approved" was not found, its assumed that the card was denied
and a business exception is thrown.

The setSessionContext() method provides the bean instance with a reference to the
SessionContext which serves the same purpose as the EntityContext did for the
CustomerBean in the section on Entity beans. The SessionContext is not used in this
example.

The ejbActivate() and ejbPassivate() methods are not implemented in the CreditService
bean because passivation is not used in stateless session beans. These methods are
defined in the javax.ejb.SessionBean interface for the Stateful session beans, and so an
empty implementation must be provided in stateless session beans. Stateless session
bean will never provide anything but empty implementations of these methods.

Stateless session beans can also be used to access the database as well as
coordinate the interaction of other bean to accomplish a task. Below is the definition
of the HotelClerkBean shown earlier in this tutorial:

import javax.ejb.SessionBean;
import javax.naming.InitialContext;

public interface HotelClerkBean implements SessionBean {

InitialContext jndiContext;

public void ejbCreate() {}

public void reserveRoom(Customer cust, RoomInfo ri,


Date from, Date to) {
CreditCard card = cust.getCreditCard();

© Trendz Information Technologies Ltd. Page No. 172 of 220


J2EE

RoomHome roomHome = (RoomHome)


getHome("java:comp/env/ejb/RoomEJB", RoomHome.class);
Room room = roomHome.findByPrimaryKey(ri.getID());
double amount = room.getPrice(from,to);

CreditServiceHome creditHome =
(CreditServiceHome) getHome(
"java:comp/env/ejb/CreditServiceEJB",
CreditServiceHome.class);
CreditService creditAgent = creditHome.create();
creditAgent.verify(card, amount);

ReservationHome resHome =
(ReservationHome) getHome(
"java:comp/env/ejb/ReservationEJB",
ReservationHome.class);
Reservation reservation = resHome.create(cust.getName(),
room,from,to);
}

public RoomInfo[] availableRooms(Location loc,


Date from, Date to) {
// do a SQL call to find available rooms
Connection con = db.getConnection();
Statement stmt = con.createStatement();
ResultSet results = stmt.executeQuery("SELECT ...");
...
return roomInfoArray;
}

private Object getHome(String path, Class type) {


Object ref = jndiContext.lookup(path);
return PortableRemoteObject.narrow(ref,type);
}
}

The HotelClerkBean is also a stateless bean. All the information needed to process a
reservation or to query a list of available rooms is obtained from the method
arguments. In the reserveRoom() method, operations on several other beans (Room,
CreditService, and Reservation) are coordinated to accomplish one larger task,
reserving a room for a customer. This is an example of a session bean managing the
interactions of other beans on behalf of the client. The availableRooms() method is
used to query the database and obtain a list of rooms -- the information is returned
to the client as a collection of data wrappers defined by the RoomInfo class. The use
of this class, shown below, is a design pattern that provides the client with a light-
weight wrapper of just the information needed.

public class RoomInfo {


public int id;
public int bedCount;
public boolean smoking;
}

© Trendz Information Technologies Ltd. Page No. 173 of 220


J2EE

To obtain a reference to another bean, as is done three times in the reserveRoom()


method, the private getHome() helper method is used. The getHome() method uses
the JNDI ENC to obtain references to other beans.

private Object getHome(String path, Class type) {


Object ref = jndiContext.lookup(path);
return PortableRemoteObject.narrow(ref,type);
}

In the EJB 1.1 specification, RMI over IIOP is the specified programming model, so
CORBA references types must be supported. CORBA references cannot be cast using
Java native casting. Instead the PortableRemoteObject.narrow() method must be used
to explicitly narrow a reference from one type to its subtype. Since JNDI always
returns an Object type, all bean references should be explicitly narrowed to support
portability between containers.

Stateful Session Beans


Stateful session beans are dedicated to one client and maintain conversational-state
between method invocations. Unlike stateless session beans, clients do not share
stateful beans. When a client creates a stateful bean, that bean instance is dedicated
to service only that client. This makes it possible to maintain conversational-state,
which is business state that can be shared by methods in the same stateful bean.

As an example, the HotelClerk bean can be modified to be a stateful bean which can
maintain conversational-state between method invocations. This would be useful, for
example, if you want the HotelClerk bean to be able to take many reservations, but
then process them together under one credit card. This occurs frequently, when
families need to reserve two or more rooms or when corporations reserve a block of
rooms for some event. Below the HotelClerkBean is modified to be a stateful bean.

import javax.ejb.SessionBean;
import javax.naming.InitialContext;

public class HotelClerkBean implements SessionBean {

InitialContext jndiContext;

//conversational-state
Customer cust;
Vector resVector = new Vector();

public void ejbCreate(Customer customer) {}


cust = customer;
}

public void addReservation(Name name, RoomInfo ri,


Date from, Date to) {
ReservationInfo resInfo =
new ReservationInfo(name,ri,from,to);
resVector.addElement(resInfo);
}

© Trendz Information Technologies Ltd. Page No. 174 of 220


J2EE

public void reserveRooms() {


CreditCard card = cust.getCreditCard();
Enumeration resEnum = resVector.elements();

while (resEnum.hasMoreElements()) {

ReservationInfo resInfo =
(ReservationInfo) resEnum.nextElement();

RoomHome roomHome = (RoomHome)


getHome("java:comp/env/ejb/RoomEJB", RoomHome.class);
Room room =
roomHome.findByPrimaryKey(resInfo.roomInfo.getID());
double amount = room.getPrice(resInfo.from,restInfo.to);

CreditServiceHome creditHome = (CreditServiceHome)


getHome("java:comp/env/ejb/CreditServiceEJB",
CreditServiceHome.class);
CreditService creditAgent = creditHome.create();
creditAgent.verify(card, amount);

ReservationHome resHome = (ReservationHome)


getHome("java:comp/env/ejb/ReservationEJB",
ReservationHome.class);
Reservation reservation =
resHome.create(resInfo.getName(),
resInfo.roomInfo,resInfo.from,resInfo.to);
}

public RoomInfo[] availableRooms(Location loc,


Date from, Date to) {
// Make an SQL call to find available rooms
}

private Object getHome(String path, Class type) {


Object ref = jndiContext.lookup(path);
return PortableRemoteObject.narrow(ref,type);
}
}

In the stateful version of the HotelClerkBean class, the conversational state is the
Customer reference, which is obtained when the bean is created, and the Vector of
ReservationInfo objects. By maintaining the conversational-state in the bean, the
client is absolved of the responsibility of keeping track of this session state. The bean
keeps track of the reservations and processes them in a batch when the
serverRooms() method is invoked.

To conserve resources, stateful session beans may be passivated when they are not
in use by the client. Passivation in stateful session beans is different then for entity
beans. In stateful beans, passivation means the bean conversational-state is written
to a secondary storage (often disk) and the instance is evicted from memory. The
client's reference to the bean is not affected by passivation, it remains alive and
usable while the bean is passivated. When the client invokes a method on a bean

© Trendz Information Technologies Ltd. Page No. 175 of 220


J2EE

that is passivated, the container will activate the bean by instantiating a new
instance and populating its conversational-state with the state written to secondary
storage. This passivation/activation process is often accomplished using simple Java
serialization but it can be implemented in other proprietary ways as long as the
mechanism behaves the same as normal serialization. (One exception to this is that
transient fields do not need to be set to their default initial values when a bean is
activated.)

Stateful session beans, unlike stateless beans, do use the ejbActivate() and
ejbPassivate() methods. The container will invoke these methods to notify the bean
when its about to passivated (ejbPassivate()) and immediately following activation
(ejbActivate()). Bean developers should use these method to close open resources
and to do other clean-up before the instance's state is written to secondary storage
and evicted from memory.

The ejbRemove() method is invoked on the stateful instance when the client invokes
the remove() method on the home or remote interface. The bean should use the
ejbRemove() method to do the same kind of clean-up performed in the ejbPassivate()
method.

Deploying Enterprise JavaBeans Technology Solutions


As mentioned previously, the container handles persistence, transactions,
concurrency, and access control automatically for the enterprise beans. The EJB
specification describes a declarative mechanism for how these things will be handled,
through the use of an XML deployment descriptor. When a bean is deployed into a
container, the container reads the deployment descriptor to find out how transaction,
persistence (entity beans), and access control should be handled. The person
deploying the bean will use this information and specify additional information to
hook the bean up to these facilities at run time.

A deployment descriptor has a predefined format that all EJB compliant beans must
use and all EJB compliant servers must know how to read. This format is specified in
an XML Document Type Definition, or DTD. The deployment descriptor describes the
type of bean (session or entity) and the classes used for the remote, home, and
bean class. It also specifies the transactional attributes of every method in the bean,
which security roles can access each method (access control), and whether
persistence in the entity beans is handled automatically or is preformed by the bean.
Below is an example of a XML deployment descriptor used to describe the Customer
bean:

<?xml version="1.0"?>

<!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise


JavaBeans 1.1//EN" "http://java.sun.com/j2ee/dtds/ejb-jar_1_1.dtd">

<ejb-jar>
<enterprise-beans>
<entity>
<description>
This bean represents a customer
</description>
<ejb-name>CustomerBean</ejb-name>
<home>CustomerHome</home>

© Trendz Information Technologies Ltd. Page No. 176 of 220


J2EE

<remote>Customer</remote>
<ejb-class>CustomerBean</ejb-class>
<persistence-type>Container</persistence-type>
<prim-key-class>Integer</prim-key-class>
<reentrant>False</reentrant>

<cmp-field><field-name>myAddress</field-name></cmp-field>
<cmp-field><field-name>myName</field-name></cmp-field>
<cmp-field><field-name>myCreditCard</field-name></cmp-field>
</entity>
</enterprise-beans>

<assembly-descriptor>
<security-role>
<description>
This role represents everyone who is allowed full access
to the Customer bean.
</description>
<role-name>everyone</role-name>
</security-role>

<method-permission>
<role-name>everyone</role-name>
<method>
<ejb-name>CustomerBean</ejb-name>
<method-name>*</method-name>
</method>
</method-permission>

<container-transaction>
<description>
All methods require a transaction
</description>
<method>
<ejb-name>CustomerBean</ejb-name>
<method-name>*</method-name>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
</assembly-descriptor>
</ejb-jar>

EJB-capable application servers usually provide tools, which can be used to build the
deployment descriptors; this greatly simplifies the process.

When a bean is to be deployed, its remote, home, and bean class files and the XML
deployment descriptor must be packaged into a JAR file. The deployment descriptor
must be stored in the JAR under the special name META-INF/ejb-jar.xml. This JAR file,
called an ejb-jar, is vendor neutral; it can be deployed in any EJB container that
supports the complete EJB specification. When a bean is deployed in an EJB
container its XML deployment descriptor is read from the JAR to determine how to
manage the bean at runtime. The person deploying the bean will map attributes of
the deployment descriptor to the container's environment. This will include mapping

© Trendz Information Technologies Ltd. Page No. 177 of 220


J2EE

access security to the environment's security system, adding the bean to the EJB
container's naming system, etc. Once the bean developer has finished deploying the
bean it will become available for client applications and other beans to use.

Enterprise JavaBeans Clients


Enterprise JavaBeans clients may be standalone applications, servlets, applets, or
even other enterprise beans. For instance, the HotelClerk session bean shown above
was a client of the Room entity beans. All clients use the server bean's home
interface to obtain references to the server bean. This reference has the server
bean's remote interface datatype, therefore the client interacts with the server bean
solely through the methods defined in the remote interface.

The remote interface defines the business methods, such as accessor and mutator
methods for changing a customer's name, or business methods that perform tasks
like using the HotelClerk bean to reserve a room at a hotel. Below is an example of
how a Customer bean might be accessed from a client application. In this case the
home interface is CustomerHome and the remote interface is Customer.

CustomerHome home;
Object ref;

// Obtain a reference to the CustomerHome


ref = jndiContext.lookup("java:comp/env/ejb/Customer");

// Cast object returned by the JNDI lookup to the


// appropriate datatype
home = PortableRemoteObject.narrow(ref, CustomerHome.class);

// Use the home interface to create a


// new instance of the Customer bean.
Customer customer = home.create(customerID);

// Use a business method on the Customer.


customer.setName(someName);

A client first obtains a reference to the home interface by using JNDI ENC to lookup
the server beans. In EJB 1.1, Java RMI-IIOP is the specified programming model. As
a consequence, all CORBA references types must be supported. CORBA references
cannot be cast using Java native casting. Instead the PortableRemoteObject.narrow()
method must be used to explicitly narrow a reference from one type to its subtype.
Since JNDI always returns an Object type, all bean references should be explicitly
narrowed to support portability between containers.

After the home interface is obtained, the client uses the methods defined on the
home interface to create, find, or remove the server bean. Invoking one of the
create() methods on the home interface returns the client a remote reference to the
server bean, which the client uses to perform its job.

© Trendz Information Technologies Ltd. Page No. 178 of 220


J2EE

EJB 2.0 FEATURES

EJB QL

EJB QL is a SQL like language used to provide a standard syntax for finder methods
to locate beans. Also provides for select methods, private query methods only
useable internally by the bean class.

Why an EJB Query Language?


The specification for Enterprise Java Beans 1.1 doesn't offer a standard way to define
queries for finder methods in entity beans with container-managed persistence
(CMP). So EJB container providers have defined their own query syntax for the finder
methods of entity beans. For example, BEA's WebLogic Application Server v. 5.1
defines a query language called WLQL.

Because of this, developers have to redefine queries for the finder methods
whenever an application is moved from one vendor's application server to another's.
Obviously, this makes applications built using CMP less portable.

What's more, EJB 1.1 offered no standard way to define a query for an entity bean
with CMP to navigate with other entity beans in a variety of code contexts,
relationships, or associations. There was no proper mechanism to define queries to
navigate from one entity bean to its dependent classes and the member variables of
those dependent classes.

EJB 2.0 deals with these shortcomings by defining the EJB Query Language. EJB QL
is based on the SQL-92 specification for defining various finder and select methods of
entity beans with CMP. The EJB QL query string consists of three clauses: SELECT,
FROM, and WHERE. Among other things, EJB QL offers a standard way to define
relationships between entity beans and dependent classes by introducing abstract
schema types and relationships in the deployment descriptor. EJB QL also defines
queries for navigation using abstract schema names and relationships.

What is EJB QL?


The following syntax is the basic format of the EJB QL query:

EJB QL ::= select_clause from_clause [ where_clause]

The EJB QL must always contain SELECT and FROM clauses. The WHERE clause is
optional. The FROM clause provides declarations for the identification variables based
on abstract schema name, for navigating through the schema. The SELECT clause
uses these identification variables to define the return type of the query, and the
WHERE clause defines the conditional query.

The query for EJB QL is defined in the deployment descriptor using the <query> tag
as shown below:

<query>
<query-method>
<method-name></method-name>
<method-params>
<method-param></method-param>

© Trendz Information Technologies Ltd. Page No. 179 of 220


J2EE

</method-params>
</query-method>
<result-type-mapping></result-type-mapping>
<ejb-ql></ejb-ql>
</query>

You specify the name of the finder or select method in <method-name> and the
parameters in <method-param>. The <result-type-mapping> indicates the return
type, and can contain either Local (the default) or Remote values. The query string is
in the <ejb-ql> tag.

I will give a brief description of the finder and select methods since these methods
use EJB QL to define the queries. EJB 2.0 defines the finder and select methods for
entity beans. The select method is a new addition to the specification.

Finder Methods: Finder methods get either a single or a collection of entity bean
instances from the persistence store through a relational database. These methods
define the home interface(s) of an entity bean. Hence, they are exposed to the
client. A home interface can either be a Remote Home interface, EJBHome, or a Local
Home interface, EJBLocalHome. The return type of the finder method defined in the
remote home interface is either the entity bean's remote interface or a collection of
objects implementing the entity bean's remote interface. The return type of the
finder method defined in the local home interface is either the entity bean's local
interface or a collection of objects implementing the entity bean's local interface. For
example:

// Remote Home Interface


public interface OrderHome extends javax.ejb.EJBHome {
...
public Order findByPrimaryKey(int orderId) throws
FinderException,RemoteException;
public Order findByBiggestOrder() throws FinderException,RemoteException;
public java.util.Collection findAllOrders(String supplierName) throws
FinderException,RemoteException;
}
// Local Home Interface
public interface OrderHome extends javax.ejb.EJBLocalHome {
...
public Order findByPrimaryKey(int orderId) throws FinderException;
public Order findBiggestOrder() throws FinderException;
public java.util.Collection findAllOrders(String supplierName) throws FinderException;
}
Select Methods: Select methods exist in the entity bean as a special type of query
method. They are not declared in the home interface; therefore, they are not
exposed to the client. The finder methods are not useful to access cmp-field, or any
remote interface instance defined in cmr-field. Using select methods, an entity bean
returns an instance of cmp-field type, or the remote interfaces represented by the
cmr-field.

Select methods are usually two types.

• ejbSelect<METHOD>
• ejbSelect<METHOD>InEntity

© Trendz Information Technologies Ltd. Page No. 180 of 220


J2EE

Example:
public abstract class OrderBean implements javax.ejb.EntityBean {
...
public abstract java.util.Collection ejbSelectAllOrderedProducts(Date date)
throws FinderException;
...
public abstract java.util.Collection ejbSelectAllOrderedProductsInEntity(Date date)
throws FinderException;
}

The ejbSelect<METHOD> is not associated with a particular instance of the entity


bean. So in the above example, the ejbSelectAllOrderProducts returns a collection of
all products associated with all orders. Therefore, the ejbSelect<METHOD>InEntity is
specific to the entity instance executed. The above ejbSelectAllOrderProductsInEntity
returns all of the products associated with that instance of OrderBean.

Here the Order example explains each clause in detail. The relationship between
OrderEJB, LineItemEJB, ProductEJB and AddressEJB are shown in the following
diagram:

The relationships, as defined in the deployment descriptor, are shown below:


<relationships>
<!--
ONE-TO-MANY: Order LineItem
-->

<ejb-relation>
<ejb-relation-name>Order-LineItem</ejb-relation-name>
<ejb-relationship-role>
<ejb-relationship-role-name>
order-has-lineitems
</ejb-relationship-role-name>
<multiplicity>One</multiplicity>
<relationship-role-source>
<ejb-name>OrderEJB</ejb-name>

© Trendz Information Technologies Ltd. Page No. 181 of 220


J2EE

</relationship-role-source>
<cmr-field>
<cmr-field-name>lineItems</cmr-field-name>
<cmr-field-type>java.util.Collection
</cmr-field-type>
</cmr-field>
</ejb-relationship-role>

<ejb-relationship-role>
<ejb-relationship-role-name>lineitem-belongsto-order
</ejb-relationship-role-name>
<multiplicity>Many</multiplicity>
<cascade-delete/>
<relationship-role-source>
<ejb-name>LineItemEJB</ejb-name>
</relationship-role-source>
<cmr-field>
<cmr-field-name>order</cmr-field-name>
</cmr-field>
</ejb-relationship-role>
</ejb-relation>
<!--
ONE-TO-MANY unidirectional relationship:
Product is not aware of its relationship with LineItem
-->
<ejb-relation>
<ejb-relation-name>Product-LineItem</ejb-relation-name>
<ejb-relationship-role>
<ejb-relationship-role-name>product-has-lineitems</ejb-relationship-role-name>
<multiplicity>One</multiplicity>
<relationship-role-source>
<ejb-name>ProductEJB</ejb-name>
</relationship-role-source>
<!-- since Product does not know about LineItem there is no cmr field in Product
for accessing Lineitem -->
</ejb-relationship-role>
<ejb-relationship-role>
<ejb-relationship-role-name>lineitem-for-product</ejb-relationship-role-name>
<multiplicity>Many</multiplicity>
<relationship-role-source>
<ejb-name>LineItemEJB</ejb-name>
</relationship-role-source>
<cmr-field>
<cmr-field-name>product</cmr-field-name>
</cmr-field>
</ejb-relationship-role>
</ejb-relation>
</relationships>

© Trendz Information Technologies Ltd. Page No. 182 of 220


J2EE

FROM Clause
The FROM clause defines the domain of the query by declaring the identification
variables. Identification variables cannot be declared in the SELECT and WHERE
clauses. Instead, the SELECT and WHERE clauses can use only those identification
variables defined in the FROM clause. You can define multiple identification variables
in the FROM clause.

Any valid identifier may be used as an identification variable, though there are few
restrictions (see sidebar). Identification variables are case insensitive.

The identification variable declaration consists of either a range variable declaration


or a collection member declaration. Next, we will look at a simple query string which
uses both the range and collection variable declarations from the FROM clause.

For example, to select all orders containing Floppy Drive products, the query is:

SELECT OBJECT(o) FROM Order o, IN (o.lineItems) li


WHERE li.product.product_type='Floppy Drive'

Here the FROM clause declares the identifier "o" as a range variable and "li" as a
collection member variable. A range variable declares the abstract schema type,
which uses the reserved identifier, AS.

A declared collection member variable uses the following:

• reserved identifier, IN
• abstract-schema-types of range variables and
• abstract-schema-types of associated entity beans.

The range variable "o" designates the abstract schema type, Order. Similarly, the
identification variables: "li" is the abstract schema type, LineItem, and li.product is
the abstract schema type, Product. The expression li.product.product_type in the
WHERE clause is a java.lang.String type. Since all clauses are evaluated from left to
right in EJB QL, the identification variable "li" utilizes the results of the navigation on
"o".

The identifier OBJECT in the SELECT clause is required, because the OBJECT operator
must qualify all stand-alone identification variables in the SELECT clause.

You may also declare a range variable using the optional identifier, AS. Therefore, the
FROM clause in the above query becomes:

FROM Order AS o, IN (o.lineItems) li

Also, you may define more than one range variable in the FROM clause. For example:

FROM Order AS o, IN (o.lineItems) li, Product p

© Trendz Information Technologies Ltd. Page No. 183 of 220


J2EE

WHERE Clause
The WHERE clause defines conditional expressions to select objects or values that
satisfy the expression.

For example, a WHERE clause follows:

Where_clause ::= WHERE conditional_expression

All of the identification variables in a WHERE clause in EJB QL must be declared from
a FROM clause. You may also pass input parameters for the finder and select
methods. The input parameters are only in the WHERE clause of a query.

Input parameters are designated by a question mark (?) prefix, followed by a one-
based index of the parameter in the method declaration (i.e., ?1, ?2).

public abstract class OrderBean implements javax.ejb.EntityBean {


...
//method-a
public abstract java.util.Collection ejbSelectLineItems(int quantity)
throws FinderException;
...
//method-b
public abstract java.util.Collection ejbSelectAllProducts(String product_type, double
price)
throws FinderException;
}

For example, the select method query for selecting Line Items (method-a) is:

SELECT OBJECT (o) FROM Order AS o IN (o.lineItems) li


WHERE li.quantity = ?1

Similarly, the select method query for choosing all products based on name and price
is:

SELECT OBJECT (o) FROM Order AS o IN(o.lineItems) li


WHERE li.product.product_type=?1 AND li.product.price=?2

The query for method-a is in the following deployment descriptor:

<query>
<description>
Method to find order specified no of lineItems</description>
<query-method>
<method-name>ejbSelectLineItems</method-name>
<method-params>
<method-param>int</method-param>
</method-params>

© Trendz Information Technologies Ltd. Page No. 184 of 220


J2EE

</query-method>
<result-type-mapping>Local</result-type-mapping>
<ejb-ql>
SELECT OBJECT (o) FROM Order AS o IN (o.lineItems) li
WHERE li.quantity = ?1
</ejb-ql>
</query>

In the same way, you may define the WHERE clause with input parameters.
The number of distinct input parameters in an EJB QL query must be the same as the
number of input parameters for the finder and select methods. It is not required to
use all of the input parameters for the finder or select methods in a query, though.

You can also pass an input parameter that corresponds to a particular EJBObject or
EJBLocalObject. Containers map these input parameters to the abstract-schema-type
values.

Next, I will show the various comparison operators available for use with the WHERE
clause. In addition to the navigation operator (.) in the queries above, EJB QL
supports the fundamental arithmetic operators (i.e., unary, multiplication and
division, addition and subtraction), comparison operators (i.e., =, >, >=, <,<=, <>)
and logical operators (i.e., NOT,OR, AND).

You can also use the comparison operators BETWEEN or NOT BETWEEN in a query.
For example, the query to select all Line Items with a quanitity between 100 and 200
is shown here:

SELECT OBJECT (li) FROM lineItems AS li


WHERE li.quantity BETWEEN 100 and 200

Using comparison operators >= and <=, the WHERE clause is now in the following
expression:

li.quantity >= 100 AND li.quantity <= 200

The following query selects all Line Items with a quantity less than 100 or more than
200:

SELECT OBJECT (li) FROM lineItems AS li


WHERE li.quantity NOT BETWEEN 100 and 200
(or)
SELECT OBJECT (li) FROM lineItems AS li
WHERE li.quantity < 100 AND li.quantity > 200

EJB QL also supports the IN and LIKE expressions. For example, to select the
address(es) of an office in various cities, the query expression of the WHERE clause
is:

address.city IN ('San Jose', 'New York', 'Florida')

The expression results are true for Florida and false for Texas.
Usage of NOT IN is just the opposite of the above:

© Trendz Information Technologies Ltd. Page No. 185 of 220


J2EE

address.city NOT IN ('San Jose', 'New York', 'Florida')

Here, the expression results are true for Texas and false for Florida.

An IN expression must contain at least one string-literal in the comma-separated


string literal list. You use the comparison operator [NOT] LIKE in the WHERE clause
to select a particular value.

The syntax of the LIKE expression is below:

single_valued_path_expression [NOT] LIKE pattern-value [ESCAPE escape-character]

The single_valued_path_expression must result in a String value. You use any string
literal in the pattern-value. An underscore (_) represents any single character and a
percent (%) any sequence of characters, including an empty sequence. The escape-
character is a single character string literal, and is for escaping the special meaning
of underscore and percent characters in pattern-value.

For example, the following query selects all employees whose names start with
CHRIS:

SELECT OBJECT (emp) FROM employee as emp


WHERE emp.name LIKE 'CHRIS%'

And to select all employees whose names don't start with CHRIS, use the following:

SELECT OBJECT (emp) FROM employee as emp


WHERE emp.name NOT LIKE 'CHRIS%'

If the value of emp.name in the above expression is NULL, then the value of the
expression is unknown. To avoid this, check whether a
single_valued_path_expression is NULL by using the IS NULL operator. The following
expression returns true when an employee's name is a NULL value.

emp.name IS NULL

EJB QL also provides the IS EMPTY comparison operator to check whether a


collection object of a query string return type contains empty values. EJB QL
provides the MEMBER [OF] identifier to check whether the value of a
single_valued_path expression is a member of collection designated by the
collection_valued_path expression.

The only identifiers that use a collection valued path expression are the comparison
expressions: IS [NOT] EMPTY and [NOT] MEMBER [OF]. Note that in EJB QL queries,
any comparison or arithmetic operations with a NULL value or an unknown value
always yields an unknown value. Path expressions with NULL values during
evaluation return NULL values.

The SELECT clause


The SELECT clause denotes the result of the query. The SELECT clause syntax is
below, in BNF format:

© Trendz Information Technologies Ltd. Page No. 186 of 220


J2EE

SELECT [DISTINCT] {single_valued_path_expression | OBJECT


(identification_variable)

DISTINCT works in the same way as SQL in selecting unique values from the query
result.

You may also restrict the return type to contain only unique values by declaring
java.util.Set as the return type for the finder or select methods. Since java.util.Set
doesn't allow duplicate values, whenever the return type is java.util.Set, the
container internally applies DISTINCT to the query. Therefore, it is not required to
explicitly use the DISTINCT identifier in the query string. But when the return type is
a java.util.Collection, then it requires an explicit DISTINCT identifier in the query
expression to get unique values.

The SELECT clause determines the type of values returned by a query. For example,
to get all orders, the query is:

SELECT OBJECT(o) FROM order o

Here, the order is the abstract-schema-name for OrderEJB.

Similarly, to get all products that are associated with a line item:

SELECT li.product FROM Order As o, IN(o.lineItems) li

These line items are the cmr-field name of LineItemsEJB.


Now, we see the following query, to get all line items related to an Order:

SELECT o.lineItems FROM Order As o

Looking carefully, the above query doesn't work, because the return type of the
query is not a single-valued expression. The earlier query to get all products
associated with line items works fine. This is because the relationship between line
item and product is one-to-one, whereas the relationship between order and line
items is one-to-many. The single valued path expression is the
single_valued_cmr_field, which is a cmr-field name in one-to-one or many-to-one
relationship. Since the relationship between order and line is one-to-many, the result
yields a collection_valued_path_expression type.

Therefore, the SELECT clause must be specified to return a single valued expression.
The SELECT clause of a query defined for a finder method must always correspond to
the abstract schema type of entity bean for which the finder method is defined. The
SELECT clause of a query defined for a select method returns abstract schema types
of other entity beans, as well as the values for cmp-fields.

Now, you will see some queries of select methods whose return type is that of cmp-
field. To select the names of all products that have been ordered:

SELECT DISTICT li.product.name FROM order o, IN (o.lineItems) li

Here, order is the abstract-type-name of OrderEJB. lineItems and products are cmr-
field names. name is the cmp-field name defined for the entity bean ProductEJB.

© Trendz Information Technologies Ltd. Page No. 187 of 220


J2EE

EJB QL also provides built-in functions for performing simple operations on objects of
String class and primitive types. Specifically, EJB QL provides the following built-in
functions:

String Functions:

• CONCAT (String, String) - This function returns the concatenated string value.
• SUBSTRING (String, start, length) - This function returns the substring of the
original string.
• LOCATE (String, String [, start]) This function returns int.
• LENGTH (String) This function returns the length of the string passed as
parameter.

Arithmetic Functions:

• ABS (number)
• SQRT (double)

Advantages

• EJB QL provides a standard way to define queries for entity beans in a


portable way. An entity bean with EJB QL defined for its finder and select
methods may be portable across any EJB container that adheres to the EJB
2.0 specification container managed persistence.
• An EJB Container parses and validates EJB QL queries before entity beans are
deployed, since EJB QL uses abstract schema types and cmr-fields to navigate
across entity beans.

Limitations

• Some of the useful features of SQL are not yet provided by EJB QL. For
example, the ORDER BY identifier, which becomes very handy as the
application becomes large and complex, is not yet supported in EJB QL. Some
of the application servers may provide these features, but usage of the same
may limit portability across application servers.
• Date and time values should be passed as millisecond value using Java
primitive type, long.
• EJB QL does not support fixed decimal comparison in arithmetic expressions.
• String and Boolean comparison is restricted to = and <>. However, the built-
in functions(CONCAT, SUBSTRING, etc.) can be used to perform other
operations on String.
• EJB QL does not support comments.

Summary
The addition of EJB QL to the new EJB 2.0 specification justifies the distributed
component architecture as the standard way of defining queries. EJB QL allows
applications to be more portable. I believe future versions of EJB QL may provide
support for more built-in functions, as well as other SQL features like ORDER BY. In
the EJB 2.0 specification, the data model for CMP does not currently support
inheritance; therefore, you cannot compare objects or value classes of different
types. This may be addressed in future versions of EJB QL.

© Trendz Information Technologies Ltd. Page No. 188 of 220


J2EE

MESSAGE DRIVEN BEANS

Stateless, server-side transaction aware components, receive and process


asynchronous messages from a JMS provider

Message-Driven Beans (MDB) are stateless, server-side, transaction-aware


components for processing asynchronous JMS messages. Newly introduced in EJB
2.0, message-driven beans process messages delivered via the Java Message
Service.

Message-driven beans can receive JMS messages and process them. While a
message-driven bean is responsible for processing messages, its container takes
care of automatically managing the component's entire environment, including
transactions, security, resources, concurrency, and message acknowledgment.

One of the most important aspects of message-driven beans is that they can
consume and process messages concurrently. This capability provides a significant
advantage over traditional JMS clients, which must be custom-built to manage
resources, transactions, and security in a multithreaded environment. The message-
driven bean containers provided by EJB manage concurrency automatically, so the
bean developer can focus on the business logic of processing the messages. The MDB
can receive hundreds of JMS messages from various applications and process them
all at the same time, because numerous instances of the MDB can execute
concurrently in the container.

A message-driven bean is a complete enterprise bean, just like a session or entity


bean, but there are some important differences. While a message-driven bean has a
bean class and XML deployment descriptor, it does not have component interfaces.
The component interfaces are absent because the message-driven bean is not
accessible via the Java RMI API; it responds only to asynchronous messages.

The ReservationProcessor EJB


The ReservationProcessor EJB is a message-driven bean that receives JMS messages
notifying it of new reservations. The ReservationProcessor EJB is an automated
version of the TravelAgent EJB that processes reservations sent via JMS by other
travel organizations. It requires no human intervention; it is completely automated.

The JMS messages that notify the ReservationProcessor EJB of new reservations
might come from another application in the enterprise or an application in some
other organization. When the ReservationProcessor EJB receives a message, it
creates a new Reservation EJB (adding it to the database), processes the payment
using the ProcessPayment EJB, and sends out a ticket. This process is illustrated in
Figure 13-3.

© Trendz Information Technologies Ltd. Page No. 189 of 220


J2EE

Figure 13-3. The ReservationProcessor EJB processing


reservations

The ReservationProcessorBean Class


Here is a partial definition of the ReservationProcessorBean class. Some methods are
left empty; they will be filled in later. Notice that the onMessage() method contains
the business logic of the bean class; it is similar to the business logic developed in
the bookPassage() method of the TravelAgent EJB in Chapter 12. Here's the code:

package com.titan.reservationprocessor;

import javax.jms.Message;
import javax.jms.MapMessage;
import com.titan.customer.*;
import com.titan.cruise.*;
import com.titan.cabin.*;
import com.titan.reservation.*;
import com.titan.processpayment.*;
import com.titan.travelagent.*;
import java.util.Date;

public class ReservationProcessorBean implements javax.ejb.MessageDrivenBean,


javax.jms.MessageListener {

MessageDrivenContext ejbContext;
Context jndiContext;

public void setMessageDrivenContext(MessageDrivenContext mdc) {


ejbContext = mdc;
try {
jndiContext = new InitialContext();
} catch(NamingException ne) {
throw new EJBException(ne);

© Trendz Information Technologies Ltd. Page No. 190 of 220


J2EE

}
}

public void ejbCreate() {}

public void onMessage(Message message) {


try {
MapMessage reservationMsg = (MapMessage)message;

Integer customerPk = (Integer)reservationMsg.getObject("CustomerID");


Integer cruisePk = (Integer)reservationMsg.getObject("CruiseID");
Integer cabinPk = (Integer)reservationMsg.getObject("CabinID");

double price = reservationMsg.getDouble("Price");

// get the credit card


Date expirationDate =
new Date(reservationMsg.getLong("CreditCardExpDate"));
String cardNumber = reservationMsg.getString("CreditCardNum");
String cardType = reservationMsg.getString("CreditCardType");
CreditCardDO card = new CreditCardDO(cardNumber,
expirationDate, cardType);

CustomerRemote customer = getCustomer(customerPk);


CruiseLocal cruise = getCruise(cruisePk);
CabinLocal cabin = getCabin(cabinPk);

ReservationHomeLocal resHome = (ReservationHomeLocal)


jndiContext.lookup("java:comp/env/ejb/ReservationHomeLocal");

ReservationLocal reservation =
resHome.create(customer, cruise, cabin, price, new Date());

Object ref = jndiContext.lookup


("java:comp/env/ejb/ProcessPaymentHomeRemote");

ProcessPaymentHomeRemote ppHome = (ProcessPaymentHomeRemote)


PortableRemoteObject.narrow(ref, ProcessPaymentHomeRemote.class);

ProcessPaymentRemote process = ppHome.create();


process.byCredit(customer, card, price);

TicketDO ticket = new TicketDO(customer,cruise,cabin,price);

deliverTicket(reservationMsg, ticket);

} catch(Exception e) {
throw new EJBException(e);
}
}

public void deliverTicket(MapMessage reservationMsg, TicketDO ticket) {

© Trendz Information Technologies Ltd. Page No. 191 of 220


J2EE

// send it to the proper destination


}
public CustomerRemote getCustomer(Integer key)
throws NamingException, RemoteException, FinderException {
// get a remote reference to the Customer EJB
}
public CruiseLocal getCruise(Integer key)
throws NamingException, FinderException {
// get a local reference to the Cruise EJB
}
public CabinLocal getCabin(Integer key)
throws NamingException, FinderException {
// get a local reference to the Cabin EJB
}

public void ejbRemove() {


try {
jndiContext.close();
ejbContext = null;
} catch(NamingException ne) { /* do nothing */ }
}
}

MessageDrivenBean Interface
The Message-Driven Bean class is required to implement the
javax.ejb.MessageDrivenBean interface, which defines callback methods similar to
those in entity and session beans. Here is the definition of the MessageDrivenBean
interface:

package javax.ejb;

public interface MessageDrivenBean extends javax.ejb.EnterpriseBean {


public void setMessageDrivenContext(MessageDrivenContext context)
throws EJBException;
public void ejbRemove() throws EJBException;
}

The setMessageDrivenContext() method is called at the beginning of the MDB's life


cycle and provides the MDB instance with a reference to its MessageDrivenContext:

MessageDrivenContext ejbContext;
Context jndiContext;

public void setMessageDrivenContext(MessageDrivenContext mdc) {


ejbContext = mdc;
try {
jndiContext = new InitialContext();
} catch(NamingException ne) {
throw new EJBException(ne);
}
}

© Trendz Information Technologies Ltd. Page No. 192 of 220


J2EE

The setMessageDrivenContext() method in the ReservationProcessorBean class sets


the ejbContext instance field to the MessageDrivenContext, which was passed into
the method. It also obtains a reference to the JNDI ENC, which it stores in the
jndiContext. MDBs may have instance fields that are similar to a stateless session
bean's instance fields. These instance fields are carried with the MDB instance for its
lifetime and may be reused every time it processes a new message. Unlike stateful
session beans, MDBs do not have conversational state and are not specific to a single
JMS client. MDB instances are used to processes messages from many different JMS
clients and are tied to a specific topic or queue from which they receive messages,
not to a specific JMS client. They are stateless in the same way that stateless session
beans are stateless.

ejbRemove() provides the MDB instance with an opportunity to clean up any


resources it stores in its instance fields. In this case, we use it to close the JNDI
context and set the ejbContext field to null. These operations are not absolutely
necessary, but they illustrate the kind of operation that an ejbRemove() method
might do. Note that ejbRemove() is called at the end of the MDB's life cycle, before it
is garbage collected. It may not be called if the EJB server hosting the MDB fails or if
an EJBException is thrown by the MDB instance in one of its other methods. When an
EJBException (or any RuntimeException type) is thrown by any method in the MDB
instance, the instance is immediately removed from memory and the transaction is
rolled back.

MessageDrivenContext
The MessageDrivenContext simply extends the EJBContext; it does not add any new
methods. The EJBContext is defined as:

package javax.ejb;
public interface EJBContext {

// transaction methods
public javax.transaction.UserTransaction getUserTransaction()
throws java.lang.IllegalStateException;
public boolean getRollbackOnly() throws java.lang.IllegalStateException;
public void setRollbackOnly() throws java.lang.IllegalStateException;

// EJB home methods


public EJBHome getEJBHome();
public EJBLocalHome getEJBLocalHome();

// security methods
public java.security.Principal getCallerPrincipal();
public boolean isCallerInRole(java.lang.String roleName);

// deprecated methods
public java.security.Identity getCallerIdentity();
public boolean isCallerInRole(java.security.Identity role);
public java.util.Properties getEnvironment();

}
Only the transactional methods the MessageDrivenContext inherits from EJBContext
are available to message-driven beans. The home methods--getEJBHome() and
getEJBLocalHome()--throw a RuntimeException if invoked, because MDBs do not

© Trendz Information Technologies Ltd. Page No. 193 of 220


J2EE

have home interfaces or EJB home objects. The security methods--


getCallerPrincipal() and isCallerInRole()--also throw a RuntimeException if invoked
on a MessageDrivenContext. When an MDB services a JMS message there is no
"caller," so there is no security context to be obtained from the caller. Remember that
JMS is asynchronous and doesn't propagate the sender's security context to the
receiver--that wouldn't make sense, since senders and receivers tend to operate in
different environments.

MDBs usually execute in a container-initiated or bean-initiated transaction, so the


transaction methods allow the MDB to manage its context. The transaction context is
not propagated from the JMS sender, but rather is either initiated by the container or
by the bean explicitly using javax.jta.UserTransaction. The transaction methods in
the EJBContext are explained in more detail in Chapter 14.

Message-driven beans also have access to their own JNDI environment naming
contexts (ENCs), which provide the MDB instances access to environment entries,
other enterprise beans, and resources. For example, the ReservationProcessor EJB
takes advantage of the JNDI ENC to obtain references to the Customer, Cruise,
Cabin, Reservation, and ProcessPayment EJBs as well as a JMS
QueueConnectionFactory and Queue for sending out tickets.

MessageListener Interface
In addition to the MessageDrivenBean interface, MDBs implement the
javax.jms.MessageListener interface, which defines the onMessage() method; bean
developers implement this method to process JMS messages received by a bean. It's
in the onMessage() method that the bean processes the JMS message:

package javax.jms;
public interface MessageListener {
public void onMessage(Message message);
}
It's interesting to consider why the MDB implements the MessageListener interface
separately from the MessageDrivenBean interface. Why not just put the onMessage()
method, MessageListener's only method, in the MessageDrivenBean interface so that
there is only one interface for the MDB class to implement? This was the solution
taken by an early proposed version of EJB 2.0. However, it was quickly realized that
message-driven beans could, in the future, process messages from other types of
systems, not just JMS. To make the MDB open to other messaging systems, it was
decided that it should implement the javax.jms.MessageListener interface separately,
thus separating the concept of the message-driven bean from the types of messages
it can process. In a future version of the specification, other types of MDB might be
available for technologies such as SMTP (email) or JAXM ( Java API for XML
Messaging) for ebXML. These technologies will use methods other than onMessage(),
which is specific to JMS.

The onMessage( ) method: Workflow and integration for B2B


The onMessage() method is where all the business logic goes. As messages arrive,
they are passed to the MDB by the container via the onMessage() method. When the
method returns, the MDB is ready to process a new message.

In the ReservationProcessor EJB, the onMessage() method extracts information


about a reservation from a MapMessage and uses that information to create a
reservation in the system:

© Trendz Information Technologies Ltd. Page No. 194 of 220


J2EE

public void onMessage(Message message) {


try {
MapMessage reservationMsg = (MapMessage)message;

Integer customerPk = (Integer)reservationMsg.getObject("CustomerID");


Integer cruisePk = (Integer)reservationMsg.getObject("CruiseID");
Integer cabinPk = (Integer)reservationMsg.getObject("CabinID");

double price = reservationMsg.getDouble("Price");

// get the credit card

Date expirationDate =
new Date(reservationMsg.getLong("CreditCardExpDate"));
String cardNumber = reservationMsg.getString("CreditCardNum");
String cardType = reservationMsg.setString("CreditCardType");
CreditCardDO card = new CreditCardDO(cardNumber,
expirationDate, cardType);

JMS is frequently used as an integration point for business-to-business applications,


so it's easy to imagine the reservation message coming from one of Titan's business
partners (perhaps a third-party processor or branch travel agency).

The ReservationProcessor EJB needs to access the Customer, Cruise, and Cabin EJBs
in order to process the reservation. The MapMessage contains the primary keys for
these entities; the ReservationProcessor EJB uses helper methods (getCustomer(),
getCruise(), and getCabin()) to look up the entity beans and obtain EJB object
references to them:

public void onMessage(Message message) {


...
CustomerRemote customer = getCustomer(customerPk);
CruiseLocal cruise = getCruise(cruisePk);
CabinLocal cabin = getCabin(cabinPk);
...
}

public CustomerRemote getCustomer(Integer key)


throws NamingException, RemoteException, FinderException {

Object ref = jndiContext.lookup("java:comp/env/ejb/CustomerHomeRemote");


CustomerHomeRemote home = (CustomerHomeRemote)
PortableRemoteObject.narrow(ref, CustomerHomeRemote.class);
CustomerRemote customer = home.findByPrimaryKey(key);
return customer;
}
public CruiseLocal getCruise(Integer key)
throws NamingException, FinderException {

CruiseHomeLocal home = (CruiseHomeLocal)


jndiContext.lookup("java:comp/env/ejb/CruiseHomeLocal");
CruiseLocal cruise = home.findByPrimaryKey(key);

© Trendz Information Technologies Ltd. Page No. 195 of 220


J2EE

return cruise;
}
public CabinLocal getCabin(Integer key)
throws NamingException, FinderException{

CabinHomeLocal home = (CabinHomeLocal)


jndiContext.lookup("java:comp/env/ejb/CabinHomeLocal");
CabinLocal cabin = home.findByPrimaryKey(key);
return cabin;
}

Once the information is extracted from the MapMessage, it is used to create a


reservation and process the payment. This is basically the same workflow that was
used by the TravelAgent EJB. A Reservation EJB is created that represents the
reservation itself, and a ProcessPayment EJB is created to process the credit card
payment:

ReservationHomeLocal resHome = (ReservationHomeLocal)


jndiContext.lookup("java:comp/env/ejb/ReservationHomeLocal");

ReservationLocal reservation =
resHome.create(customer, cruise, cabin, price, new Date());

Object ref =
jndiContext.lookup("java:comp/env/ejb/ProcessPaymentHomeRemote");

ProcessPaymentHomeRemote ppHome = (ProcessPaymentHomeRemote)


PortableRemoteObject.narrow (ref, ProcessPaymentHomeRemote.class);

ProcessPaymentRemote process = ppHome.create();


process.byCredit(customer, card, price);

TicketDO ticket = new TicketDO(customer,cruise,cabin,price);

deliverTicket(reservationMsg, ticket);

This illustrates that, like a session bean, the MDB can access any other entity or
session bean and use that bean to complete a task. In this way, the MDB fulfills its
role as an integration point in B2B application scenarios. MDB can manage a process
and interact with other beans as well as resources. For example, it is commonplace
for an MDB to use JDBC to access a database based on the contents of the message
it is processing.

Sending messages from a Message-Driven Bean


MDB can also send messages using JMS. The deliverTicket() method sends the ticket
information to a destination defined by the sending JMS client:

public void deliverTicket(MapMessage reservationMsg, TicketDO ticket)


throws NamingException, JMSException{

Queue queue = (Queue)reservationMsg.getJMSReplyTo();

QueueConnectionFactory factory = (QueueConnectionFactory)

© Trendz Information Technologies Ltd. Page No. 196 of 220


J2EE

jndiContext.lookup("java:comp/env/jms/QueueFactory");

QueueConnection connect = factory.createQueueConneciton();

QueueSession session = connect.createQueueSession(true,0);

QueueSender sender = session.createSender(queue);

ObjectMessage message = session.createObjectMessage();


message.setObject(ticket);

sender.send(message);

connect.close();

As stated earlier, every message type has two parts: a message header and a
message body (a.k.a. payload). The message header contains routing information
and may also have properties for message filtering and other attributes, including a
JMSReplyTo attribute. When a JMS client sends a message, it may set the
JMSReplyTo attribute to be any destination accessible to its JMS provider. In the case
of the reservation message, the sender set the JMSReplyTo attribute to the queue to
which the resulting ticket should be sent. Another application can access this queue
to read tickets and distribute them to customers or store the information in the
sender's database.

You can also use the JMSReplyTo address to report business errors that occur while
processing the message. For example, if the Cabin is already reserved, the
ReservationProcessor EJB might send an error message to the JMSReplyTo queue
explaining that the reservation could not be processed. Including this type of error
handling is left as an exercise for the reader.

XML Deployment Descriptor


MDBs have XML deployment descriptors, just like entity and session beans. They can
be deployed alone or, more often than not, together with other enterprise beans. For
example, the ReservationProcessor EJB would have to be deployed in the same JAR
using the same XML deployment descriptor as the Customer, Cruise, and Cabin beans
if it's going to use their local interfaces.

Here's the XML deployment descriptor that defines the ReservationProcessor EJB.
This deployment descriptor also defines the Customer, Cruise, Cabin, and other
beans, but these are left out here for brevity:

<enterprise-beans>
...
<message-driven>
<ejb-name>ReservationProcessorEJB</ejb-name>
<ejb-class>
com.titan.reservationprocessor.ReservationProcessorBean
</ejb-class>
<transaction-type>Container</transaction-type>
<message-selector>MessageFormat = 'Version 3.4'</message-selector>

© Trendz Information Technologies Ltd. Page No. 197 of 220


J2EE

<acknowledge-mode>Auto-acknowledge</acknowledge-mode>
<message-driven-destination>
<destination-type>javax.jms.Queue</destination-type>
</message-driven-destination>
<ejb-ref>
<ejb-ref-name>ejb/ProcessPaymentHomeRemote</ejb-ref-name>
<ejb-ref-type>Session</ejb-ref-type>
<home>com.titan.processpayment.ProcessPaymentHomeRemote</home>
<remote>com.titan.processpayment.ProcessPaymentRemote</remote>
</ejb-ref>
<ejb-ref>
<ejb-ref-name>ejb/CustomerHomeRemote</ejb-ref-name>
<ejb-ref-type>Entity</ejb-ref-type>
<home>com.titan.customer.CustomerHomeRemote</home>
<remote>com.titan.customer.CustomerRemote</remote>
</ejb-ref>
<ejb-local-ref>
<ejb-ref-name>ejb/CruiseHomeLocal</ejb-ref-name>
<ejb-ref-type>Entity</ejb-ref-type>
<local-home>com.titan.cruise.CruiseHomeLocal</local-home>
<local>com.titan.cruise.CruiseLocal</local>
</ejb-local-ref>
<ejb-local-ref>
<ejb-ref-name>ejb/CabinHomeLocal</ejb-ref-name>
<ejb-ref-type>Entity</ejb-ref-type>
<local-home>com.titan.cabin.CabinHomeLocal</local-home>
<local>com.titan.cabin.CabinLocal</local>
</ejb-local-ref>
<ejb-local-ref>
<ejb-ref-name>ejb/ReservationHomeLocal</ejb-ref-name>
<ejb-ref-type>Entity</ejb-ref-type>
<local-home>com.titan.reservation.ReservationHomeLocal</local-home>
<local>com.titan.reservation.ReservationLocal</local>
</ejb-local-ref>
<security-identity>
<run-as>
<role-name>everyone</role-name>
</run-as>
</security-identity>
<resource-ref>
<res-ref-name>jms/QueueFactory</res-ref-name>
<res-type>javax.jms.QueueConnectionFactory</res-type>
<res-auth>Container</res-auth>
</resource-ref>
</message-driven>
...
</enterprise-beans>

An MDB is declared in a <message-driven> element within the <enterprise-beans>


element, alongside <session> and <entity> beans. Similar to <session> bean types,
it defines an <ejb-name>, <ejb-class>, and <transaction-type>, but it does not
define component interfaces (local or remote). MDBs do not have component
interfaces, so these definitions aren't needed.

© Trendz Information Technologies Ltd. Page No. 198 of 220


J2EE

<message-selector>

An MDB can also declare a <message-selector> element, which is unique to


message-driven beans:

<message-selector>MessageFormat = 'Version 3.4'</message-selector>

Message selectors allow an MDB to be more selective about the messages it receives
from a particular topic or queue. Message selectors use Message properties as
criteria in conditional expressions. (Message selectors are also based on message
headers, which are outside the scope of this chapter.) These conditional expressions
use Boolean logic to declare which messages should be delivered to a client.

Message properties, upon which message selectors are based, are additional headers
that can be assigned to a message. They give the application developer or JMS
vendor the ability to attach more information to a message. The Message interface
provides several accessor and mutator methods for reading and writing properties.
Properties can have a String value or one of several primitive values (boolean, byte,
short, int, long, float, double). The naming of properties, together with their values
and conversion rules, is strictly defined by JMS.

The ReservationProcessor EJB uses a message selector filter to select messages of a


specific format. In this case the format is "Version 3.4"; this is a string Titan uses to
identify messages of type MapMessage that contain the name values CustomerID,
CruiseID, CabinID, CreditCard, and Price. In other words, by specifying a
MessageFormat on every reservation message, we can write MDBs that are designed
to process different kinds of reservation messages. If a new business partner needs
to use a different type of Message object, Titan would use a new message version
and an MDB to process it.

This is how a JMS producer would go about setting a MessageFormat property on a


Message:

Message message = session.createMapMessage();


message.setStringPropery("MessageFormat","Version 3.4");

// set the reservation named values

sender.send(message);

The message selectors are based on a subset of the SQL-92 conditional expression
syntax that is used in the WHERE clauses of SQL statements. They can become fairly
complex, including the use of literal values, Boolean expressions, unary operators,
and so on.

Message selector examples


Here are three complex selectors used in hypothetical environments. Although you
will have to use your imagination a little, the purpose of these examples is to convey
the power of the message selectors. When a selector is declared, the identifier
always refers to a property name or JMS header name. For example, the selector
UserName !='William' assumes that there is a property in the message named
UserName, which can be compared to the value 'William'.

© Trendz Information Technologies Ltd. Page No. 199 of 220


J2EE

Managing claims in an HMO


Due to some fraudulent claims, an automatic process is implemented using MDBs
that will audit all claims submitted by patients who are employees of the ACME
manufacturing company for visits to chiropractors, psychologists, and
dermatologists:

<message-selector>
<![CDATA[
PhysicianType IN ('Chiropractic','Psychologists','Dermatologist')
AND PatientGroupID LIKE 'ACME%'
]]>
</message-selector>

TIP: MDB <message-selector> statements are declared in XML


deployment descriptors. XML assigns special meaning to a variety of
characters, such as the greater than (>) and less than (<) symbols, so
using these symbols in the <message-selector> statements will cause
parsing errors unless CDATA sections are used. This is the same
reason CDATA sections were needed in EJB QL <ejb-ql> statements,
as explained in Chapter 8.

Notification of certain bids on inventory


A supplier wants notification of requests for bids on specific inventory items at
specific quantities:

<message-selector>
<![CDATA[
InventoryID ='S93740283-02' AND Quantity BETWEEN 1000 AND 13000
]]>
</message-selector>

Selecting recipients for a catalog mailing


An online retailer wants to deliver a special catalog to any customer that orders more
than $500.00 worth of merchandise where the average price per item ordered is
greater than $75.00 and the customer resides in one several states. The retailer
creates an MBD that subscribes to the order-processing topic and processes catalog
deliveries for only those customers that meet the defined criteria:

<message-selector>
<![CDATA[
TotalCharge >500.00 AND ((TotalCharge /ItemCount)>=75.00)
AND State IN ('MN','WI','MI','OH')
]]>
</message-selector>

<acknowledge-mode>
JMS has the concept of acknowledgment, which means that the JMS client notifies
the JMS provider (message router) when a message is received. In EJB, it's the MDB
container's responsibility to send an acknowledgment to the JMS provider when it
receives a message. Acknowledging a message tells the JMS provider that MDB
container has received the message and processed it using an MDB instance. Without
an acknowledgment, the JMS provider will not know whether the MDB container has

© Trendz Information Technologies Ltd. Page No. 200 of 220


J2EE

received the message, so it will try to redeliver it. This can cause problems. For
example, once we have processed a reservation message using the
ReservationProcessor EJB, we don't want to receive the same message again.

When transactions are involved, the acknowledgment mode set by the bean provider
is ignored. In this case, the acknowledgment is performed within the context of the
transaction. If the transaction succeeds, the message is acknowledged. If the
transaction fails, the message is not acknowledged. If the MDB is using container-
managed transactions, as it will in most cases, the acknowledgment mode is ignored
by the MDB container. When using container-managed transactions with a Required
transaction attribute, the <acknowledge-mode> is usually not specified; however, we
included it in the deployment descriptor for the sake of discussion:

<acknowledge-mode>Auto-acknowledge</acknowledge-mode>

When the MDB executes with bean-managed transactions, or with the container-
managed transaction attribute NotSupported (see Chapter 14), the value of
<acknowledge-mode> becomes important.

Two values can be specified for <acknowledge-mode>: Auto-acknowledge and Dups-


ok-acknowledge. Auto-acknowledge tells the container that it should send an
acknowledgment to the JMS provider soon after the message is given to an MDB
instance to process. Dups-ok-acknowledge tells the container that it doesn't have to
send the acknowledgment immediately; any time after the message is given to the
MDB instance will be fine. With Dups-ok-acknowledge, it's possible for the MDB
container to delay acknowledgment so long that the JMS provider assumes that the
message was not received and sends a "duplicate" message. Obviously, with Dups-
ok-acknowledge, your MDBs must be able to handle duplicate messages correctly.

Auto-acknowledge avoids duplicate messages because the acknowledgment is sent


immediately. Therefore, the JMS provider won't send a duplicate. Most MDBs use
Auto-acknowledge, to avoid processing the same message twice. Dups-ok-
acknowledge exists because it may allow a JMS provider to optimize its use of the
network. In practice, though, the overhead of an acknowledgment is so small, and
the frequency of communication between the MDB container and JMS provider is so
high, that Dups-ok-acknowledge doesn't have a big impact on performance.

<message-driven-destination>
The <message-driven-destination> element designates the type of destination from
which the MDB receives messages. The allowed values for this element are
javax.jms.Queue and javax.jms.Topic. In the ReservationProcessor EJB this value is
set to javax.jms.Queue, indicating that the MDB is getting its messages via the p2p
messaging model from a queue:

<message-driven-destination>
<destination-type>javax.jms.Queue</destination-type>
</message-driven-destination>

When the MDB is deployed, the deployer will map the MDB so that it listens to a real
queue on the network.

When the <destination-type> is a javax.jms.Topic, the <subscription-durability>


element must be declared with either Durable or NonDurable as its value:

© Trendz Information Technologies Ltd. Page No. 201 of 220


J2EE

<message-driven-destination>
<destination-type>javax.jms.Topic</destination-type>
<subscription-durability>Durable</subscription-durability>
</message-driven-destination>
The <subscription-durability> element determines whether or not the MDB's
subscription to the topic is Durable. A Durable subscription outlasts an MDB
container's connection to the JMS provider, so if the EJB server suffers a partial
failure, is shut down, or is otherwise disconnected from the JMS provider, the
messages that it would have received will not be lost. While a Durable MDB container
is disconnected from the JMS provider, it is the responsibility of the JMS provider to
store any messages the subscriber misses. When the Durable MDB container
reconnects to the JMS provider, the JMS provider sends it all the unexpired messages
that accumulated while it was down. This behavior is commonly referred to as store-
and-forward messaging. Durable MDBs are tolerant of disconnections, whether they are
intentional or the result of a partial failure.

If <subscription-durability> is NonDurable, any messages the bean would have


received while it was disconnected will be lost. Developers use NonDurable
subscriptions when it is not critical that all messages be processed. Using a
NonDurable subscription improves the performance of the JMS provider but
significantly reduces the reliability of the MDBs.

When <destination-type> is javax.jms.Queue, as is the case in the


ReservationProcessor EJB, durability is not a factor because of the nature of p2p or
queue-based messaging systems. With a queue, messages may be consumed only
once and remain in the queue until they are distributed to one of the queue's
listeners.

The rest of the elements in the deployment descriptor should already be familiar. The
<ejb-ref> element provides JNDI ENC bindings for a remote EJB home object while
the <ejb-local-ref> elements provide JNDI ENC bindings for local EJB home objects.
Note that the <resource-ref> element that defined the JMS QueueConnectionFactory
used by the ReservationProcessor EJB to send ticket messages is not accompanied
by a <resource-env-ref> element. The queue to which the tickets are sent is
obtained from the JMSReplyTo header of the MapMessage itself, and not from the
JNDI ENC.

The ReservationProcessor clients


In order to test the ReservationProcessor EJB, we need to develop two new client
applications: one to send reservation messages and the other to consume ticket
messages produced by the ReservationProcessor EJB.

The reservation message producer


The JmsClient_ReservationProducer is designed to send 100 reservation requests
very quickly. The speed with which it sends these messages will force many MDB
containers to use multiple instances to process the reservation messages. The code
for JmsClient_ReservationProducer looks like this:

import javax.jms.Message;
import javax.jms.MapMessage;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueConnection;

© Trendz Information Technologies Ltd. Page No. 202 of 220


J2EE

import javax.jms.QueueSession;
import javax.jms.Session;
import javax.jms.Queue;
import javax.jms.QueueSender;
import javax.jms.JMSException;
import javax.naming.InitalContext;
import java.util.Date;

import com.titan.processpayment.CreditCardDO;

public class JmsClient_ReservationProducer {

public static void main(String [] args) throws Exception {

InitialContext jndiContext = getInitialContext();

QueueConnectionFactory factory = (QueueConnectionFactory)


jndiContext.lookup("QueueFactoryNameGoesHere");

Queue reservationQueue = (Queue)


jndiContext.lookup("QueueNameGoesHere");

QueueConnection connect = factory.createQueueConneciton();

QueueSession session =
connect.createQueueSession(false,Session.AUTO_ACKNOWLEDGE);

QueueSender sender = session.createSender(reservationQueue);

Integer cruiseID = new Integer(1);

for(int i = 0; i < 100; i++){


MapMessage message = session.createMapMessage();
message.setStringProperty("MessageFormat","Version 3.4");

message.setInt("CruiseID",1);
message.setInt("CustomerID",i%10);
message.setInt("CabinID",i);
message.setDouble("Price", (double)1000+i);

// the card expires in about 30 days


Date expirationDate = new Date(System.currentTimeMillis()+43200000);
message.setString("CreditCardNum", "923830283029");
message.setLong("CreditCardExpDate", expirationDate.getTime());
message.setString("CreditCardType", CreditCardDO.MASTER_CARD);

sender.send(message);

connect.close();
}

© Trendz Information Technologies Ltd. Page No. 203 of 220


J2EE

public static InitialContext getInitialContext()


throws JMSException {
// create vendor-specific JNDI context here
}
}

You may have noticed that the JmsClient_ReservationProducer sets the CustomerID,
CruiseID, and CabinID as primitive int values, but the ReservationProcessorBean
reads these values as java.lang.Integer types. This is not a mistake. The
MapMessage automatically converts any primitive to its proper wrapper if that
primitive is read using MapMessage.getObject(). So, for example, a named value
that is loaded into a MapMessage using setInt() can be read as an Integer using
getObject(). For example, the following code sets a value as a primitive int and then
accesses it as a java.long.Integer object:

MapMessage mapMsg = session.createMapMessage();

mapMsg.setInt("TheValue",3);

Integer myInteger = (Integer)mapMsg.getObject("TheValue");

if(myInteger.intValue() == 3 )
// this will always be true

The ticket message consumer


The JmsClient_TicketConsumer is designed to consume all the ticket messages
delivered by ReservationProcessor EJB instances to the queue. It consumes the
messages and prints out the descriptions:

import javax.jms.Message;
import javax.jms.ObjectMessage;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueConnection;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.jms.Queue;
import javax.jms.QueueReceiver;
import javax.jms.JMSException;
import javax.naming.InitalContext;

import com.titan.travelagent.TicketDO;

public class JmsClient_TicketConsumer


implements javax.jms.MessageListener {

public static void main(String [] args) throws Exception {

new JmsClient_TicketConsumer();

while(true){Thread.sleep(10000);}

© Trendz Information Technologies Ltd. Page No. 204 of 220


J2EE

public JmsClient_TicketConsumer() throws Exception {

InitialContext jndiContext = getInitialContext();

QueueConnectionFactory factory = (QueueConnectionFactory)


jndiContext.lookup("QueueFactoryNameGoesHere");

Queue ticketQueue = (Queue)jndiContext.lookup("QueueNameGoesHere");

QueueConnection connect = factory.createQueueConneciton();

QueueSession session =
connect.createQueueSession(false,Session.AUTO_ACKNOWLEDGE);

QueueReceiver receiver = session.createReceiver(ticketQueue);

receiver.setMessageListener(this);

connect.start();
}

public void onMessage(Message message) {


try {

ObjectMessage objMsg = (ObjectMessage)message;


TicketDO ticket = (TicketDO)objMsg.getObject();
System.out.println("********************************");
System.out.println(ticket);
System.out.println("********************************");

} catch(JMSException jmsE) {
jmsE.printStackTrace();
}
}
public static InitialContext getInitialContext() throws JMSException {
// create vendor-specific JNDI context here
}
}
To make the ReservationProcessor EJB work with the two client applications,
JmsClient_ReservationProducer and JmsClient_TicketConsumer, you must configure
your EJB container's JMS provider so that it has two queues: one for reservation
messages and another for ticket messages.

The Life Cycle of a Message-Driven Bean


Just as the entity and session beans have well-defined life cycles, so does the MDB
bean. The MDB instance's life cycle has two states: Does Not Exist and Method-Ready
Pool. The Method-Ready Pool is similar to the instance pool used for stateless session
beans. Like stateless beans, MDBs define instance pooling in their life cycles.
Figure 13-4 illustrates the states and transitions that an MDB instance goes through
in its lifetime.

© Trendz Information Technologies Ltd. Page No. 205 of 220


J2EE

Figure 13-4. MDB life cycle

Does Not Exist


When an MDB instance is in the Does Not Exist state, it is not an instance in the
memory of the system. In other words, it has not been instantiated yet.

The Method-Ready Pool


MDB instances enter the Method-Ready Pool as the container needs them. When the
EJB server is first started, it may create a number of MDB instances and enter them
into the Method-Ready Pool. (The actual behavior of the server depends on the
implementation.) When the number of MDB instances handling incoming messages is
insufficient, more can be created and added to the pool.

Transitioning to the Method-Ready Pool


When an instance transitions from the Does Not Exist state to the Method-Ready
Pool, three operations are performed on it. First, the bean instance is instantiated
when the container invokes the Class.newInstance() method on the MDB class.
Second, the setMessageDrivenContext() method is invoked by the container
providing the MDB instance with a reference to its EJBContext. The
MessageDrivenContext reference may be stored in an instance field of the MDB.

Finally, the no-argument ejbCreate() method is invoked by the container on the bean
instance. The MDB has only one ejbCreate() method, which takes no arguments. The
ejbCreate() method is invoked only once in the life cycle of the MDB.

MDBs are not subject to activation, so they can maintain open connections to
resources for their entire life cycles. The ejbRemove() method should close any open
resources before the MDB is evicted from memory at the end of its life cycle.

Life in the Method-Ready Pool


Once an instance is in the Method-Ready Pool, it is ready to handle incoming
messages. When a message is delivered to an MDB, it is delegated to any available
instance in the Method-Ready Pool. While the instance is executing the request, it is
unavailable to process other messages. The MDB can handle many messages
simultaneously, delegating the responsibility of handling each message to a different
MDB instance. When a message is delegated to an instance by the container, the
MDB instance's MessageDrivenContext changes to reflect the new transaction

© Trendz Information Technologies Ltd. Page No. 206 of 220


J2EE

context. Once the instance has finished, it is immediately available to handle a new
message.

Transitioning out of the Method-Ready Pool: The death of an MDB instance


Bean instances leave the Method-Ready Pool for the Does Not Exist state when the
server no longer needs them. This occurs when the server decides to reduce the total
size of the Method-Ready Pool by evicting one or more instances from memory. The
process begins by invoking the ejbRemove() method on the instance. At this time,
the bean instance should perform any cleanup operations, such as closing open
resources. The ejbRemove() method is invoked only once in the life cycle of an MDB
instance--when it is about to transition to the Does Not Exist state. During the
ejbRemove() method, the MessageDrivenContext and access to the JNDI ENC are
still available to the bean instance. Following the execution of the ejbRemove()
method, the bean is dereferenced and eventually garbage collected.

What's New in EJB 2.1?


Only a few J2EE application servers are following the EJB 2.0 specification, and
already the EJB 2.1 draft specification is out. For you busy folks who want to know
about what the future has in store for EJBs but don't have the time to read a 636-
page document, here is a quick overview. Fair warning: the specification is a draft, so
many parts are incomplete or will change.

Quick List of New Features

• Message-driven beans (MDBs): can now accept messages from sources other
than JMS.
• EJB query language (EJB-QL): many new functions are added to this
language: ORDER BY, AVG, MIN, MAX, SUM, COUNT, and MOD.
• Support for Web services: stateless session beans can be invoked over
SOAP/HTTP. Also, an EJB can easily access a Web service using the new
service reference.
• EJB timer service: a new event-based mechanism for invoking EJBs at specific
times.
• Many small changes: support for the latest versions of Java specifications,
XML schema, and message destinations.

Message-driven Bean Enhancements


When the EJB 2.0 specification appeared, it introduced the idea of calling an EJB
asynchronously, but you had to do it with JMS. JMS is not the only way to invoke an
object asynchronously, however; other APIs exist, such as JAXM. How is this done
with EJB 2.1?

There are two things that differ. First, as you may know, in order to write an MDB
class, it must implement some interfaces. These are javax.ejb.MessageDrivenBean
and javax.jms.MessageListener. The latter is the interface that enables the EJB
container to subscribe the bean to the JMS server. To make your EJB listen to
another type of messaging server, the MDB must implement another interface. For
example, in order to listen to JAXM messages, the MDB must implement
javax.xml.messaging.OneWayListener or javax.xml.messaging.ReqRespListener.

As for the second difference, obviously, the configuration side of a JMS-MDB will
differ from a non-JMS one. The EJB container must know which destination or

© Trendz Information Technologies Ltd. Page No. 207 of 220


J2EE

endpoint to which it must connect. The configuration of the MDB is done with a new
<messaging-type> tag, and by specifying "configuration properties" with the tag
<activation-config-propertyType>. This contains arbitrary name/value pairs that are
specific to the messaging service being used. It is more versatile than being forced to
use JMS-specific tags, like <message-selector>.

The way non-JMS servers will plug into the EJB container is more or less overlooked
in the current draft version of the specification. There is a mention of using the J2EE-
CA, but no details are provided. This will probably be left to the EJB container to
implement, just like persistence services.

EJB-QL Enhancements
When EJB-QL came out as a standard way to write queries, it was criticized for being
a reinvention of the wheel. Don't we have already some querying languages, like SQL
and XQuery? Plus, EJB-QL lacks several features that made it less than useful, in
some cases. EJB 2.1 is trying to remedy these problems by extending the language
to make it more SQL-like. Here are a few clauses and functions that are now added
to this language.

ORDER BY
Ordering is something that's usually more optimized when it's done by the database
than when it's done on the client side. This new clause simply works by specifying
which fields to sort on, and if data needs to be sorted in ascending or descending
order. For example:

SELECT OBJECT(c) FROM ConsultantBean AS c ORDER BY c.city,c.startingDate DESC


returns all consultants sorted by city, starting with the most senior.

MOD
This numeric function returns the rest of the division between an integer and another
integer (modulo). For example:

SELECT OBJECT(p) FROM PageBean AS p WHERE MOD(p.number,2) = 1


returns all odd-numbered pages.

AVG
AVG is an aggregate function that can be used for ejbSelect methods in the SELECT
clause. It returns the average value of a specified field. For example:

SELECT AVG(e.salary) FROM EmployeeBean AS e


gets the average salary for all employees.

MIN
This aggregate function can be used for ejbSelect methods in the SELECT clause. It
returns the minimum value of a specified field. For example:

SELECT MIN(f.nextFlightDate) FROM FlightBean AS f WHERE f.departureAirport = ?1


AND f.arrivalAirport = ?2
gets the date when the next flight is scheduled between two cities.

MAX
This aggregate function can be used for ejbSelect methods in the SELECT clause. It
returns the maximum value of a specified field. For example:

© Trendz Information Technologies Ltd. Page No. 208 of 220


J2EE

SELECT MAX(c.age) FROM ClientBean AS c


returns the age of the oldest client.

SUM
This aggregate function can be used for ejbSelect methods in the SELECT clause. It
adds up all values for a field. For example:

SELECT SUM(q.sales) FROM QuarterBean AS q WHERE q.year = ?1


calculates the total of all sales for a certain year.

COUNT
This aggregate function counts the number of elements. It can only be used in
ejbSelect methods and in the SELECT clause. For example:

SELECT COUNT(s) FROM StoreBean AS s WHERE s.city = ?1


counts the number of stores in a certain city.

About Aggregate Functions


The aggregate functions (AVG, MIN, MAX, SUM, and COUNT) can be used with
DISTINCT to eliminate duplicates before calculating.

Personally, I don't see why all of these aggregate functions cannot be used in the
WHERE clause, as well. The specification says these can be used in the SELECT
clause only. It could be useful to test each record against a condition that uses an
aggregate function. For example, getting all employees whose salary is above
average:

SELECT OBJECT(e) FROM EmployeeBean AS e WHERE e.salary > AVG(e.salary)


One can work around this with an ejbSelect method, or in other ways.

Web Services
One of the best features of EJB 2.1 is the support for Web services. This applies to
two different areas: accessing an EJB as if it were a Web service, and an EJB directly
accessing a Web service.

EJB As Web Service


A stateless session EJB can now be accessed through a Web service. In order to do
that, the client must use SOAP over HTTP or HTTPS, and use a WSDL descriptor.
Furthermore, the stateless session bean must be invoked "RPC-style." To help with
the development of Web-service-enabled beans, the specification was changed in
these ways:

• SessionContext.getMessageContext() was added, to get a


javax.xml.rpc.handler.MessageContext from within the bean implementation.
• The container is now required to provide a tool that can produce Web service
facades in front of stateless session beans (called "Web Service endpoint
interface implementation class").
• The home interface is now optional, while the ejbCreate() method is still
needed.
• The specification stipulates that for each stateless session bean, a WSDL file
must be provided, and its interfaces must match the bean's methods. This
WSDL file can be generated by the container.

© Trendz Information Technologies Ltd. Page No. 209 of 220


J2EE

• A new <service-endpoint> element has been added to ejb-jar.xml.


• The Mandatory transaction attribute is not accepted for stateless-session-
bean Web services. This is understandable, because transactions cannot be
propagated from the client to the bean, as in RMI invocations.
• <method-intf> accepts ServiceEndPoint as a value.
• Exceptions are transformed into SOAP faults. Note that the server has no
control over the client's transactions, so it cannot roll them back if a system
exception happens.
• There doesn't seem to be any special note regarding security. But the
credentials cannot be sent from the client to the bean the way RMI does, so I
suspect that the usage of declarative security (using <method-permission>)
will require the use of the <run-as> tag.

Forcing the use of HTTP is somewhat restrictive, since Web services are supposed to
accept any protocol and technology, but this follows the general trend. By spelling
out these restrictions, though, I think we will get into the same trap as JMS-only
message-driven beans. The specification will later have to change to accommodate
more standards.

Many people have pointed out the disconnection between what exists in the Web
service realm and what is available in EJBs. Here are a few examples:

• How can the client's identity be propagated to the EJB? How can one set
permissions?
• How can the client demarcate transactions?
• How can the client perform connection-based services (stateful session) or
obtain data (entity)?

While these things are not addressed in the specification, they are not addressed in
SOAP, either. There are programmatic solutions around those limitations, and it will
be up to the container providers and/or bean developers to invent these solutions.

EJB Accessing a Web Service


An EJB can directly access a Web service, simply as a regular Web service client
does. But this means the Web service's location has to be hard-coded in the bean
implementation's code. This is not ideal, because if the Web service moves to a new
location, the EJB must be modified and recompiled. To work around this problem, the
new EJB 2.1 specification introduces the concept of a Web service reference. This is
exactly the same concept as environment entries, EJB references, and resource
references. Adding a Web service reference in ejb-jar.xml is done using the new tag
<service-ref>. It is up to the container provider to map the reference to the actual
Web service location. The reference can be looked up in the bean implementation,
and the resulting factory object can be used to connect to the Web service.

EJB Timer Service


The new EJB 2.1 timer service is a mechanism to enable time-based business logic.
For example, one could want to automatically:

• Run a certain query in five minutes.


• Delete the content of the "garbage can" database table one hour from now,
and then every two hours.

© Trendz Information Technologies Ltd. Page No. 210 of 220


J2EE

• Post an email saying "server shutdown in 1 hour" to all employees next


Saturday at 11 pm.
• Send a usage report to the administrator every Monday at 8 am.

Note that this timer service is not meant to be used in real-time systems.
Notifications will be sent at approximate times.

To set up a timer object, use the method EJBContext.getTimerService(), returning a


javax.ejb.TimerService object. This object is a factory for creating javax.ejb.Timer
objects. Once such an object is created, the container will take care of calling the
bean at the right time. Timer objects can later be retrieved using the TimerService
object or with a TimerHandle (Serializable), and they can be cancelled and
reconfigured.

Only "stateless" objects (stateless session beans, pooled entity beans) can process
timer events. Their bean implementations must implement the
javax.ejb.TimedObject interface, which contains a single method: void
ejbTimeout(Timer). Also, the security identity during the timer call can be specified
using <run-as>; othrwise, permissions cannot be verified.

Lastly, it is not clear how the EJB container provider will know which bean is linked to
which timer. Timer creation methods do not have parameters to specify which bean
would consume the ejbTimeout events. Plus it would be nice to have the choice of
setting up timers declaratively in ejb-jar.xml (this would take effect at deployment
time).

Other New Features


Here's a list of (smaller) new features:
• EJB containers must now support the newest specifications: J2SE 1.4, JMS
1.1, JavaMain 1.3, JAF 1.0, JAXP 1.2, JAXR 1.0, and JAX-RPC 1.0.
• The ejb-jar.xml standard deployment descriptor is now specified with XML
schema, rather than DTD.
• Message destinations (the same idea as EJB references, resource references,
etc.) have been added.

J2EE DESIGN PATTERNS

Model – View – Controller:

© Trendz Information Technologies Ltd. Page No. 211 of 220


J2EE

Despite its relatively recent introduction, Java Server Pages (JSP) technology is well
on its way to becoming the preeminent Java technology for building applications that
serve dynamic Web content. Java developers love JSP for myriad reasons. Some like
the fact that it brings the "write once, run anywhere" paradigm to interactive Web
pages; others appreciate the fact that it is fairly simple to learn and lets them wield
Java as a server-side scripting language. But most concur on one thing -- the biggest
advantage of using JSP is that it helps effectively separate presentation from
content. In this article, I provide an in-depth look at how you can gain optimal
separation of presentation from content by using the JSP Model 2 architecture. This
model can also be seen as a server-side implementation of the popular Model-View-
Controller (MVC) design pattern. Please note that you should be familiar with the
basics of JSP and servlet programming before continuing on, as I do not address any
syntax issues in this article.

So, what's wrong with servlets?


While JSP may be great for serving up dynamic Web content and separating content
from presentation, some may still wonder why servlets should be cast aside for JSP.
The utility of servlets is not in question. They are excellent for server-side
processing, and, with their significant installed base, are here to stay. In fact,
architecturally speaking, you can view JSP as a high-level abstraction of servlets that
is implemented as an extension of the Servlet 2.1 API. Still, you shouldn't use
servlets indiscriminately; they may not be appropriate for everyone. For instance,
while page designers can easily write a JSP page using conventional HTML or XML
tools, servlets are more suited for back-end developers because they are often
written using an IDE -- a process that generally requires a higher level of
programming expertise. When deploying servlets, even developers have to be careful
and ensure that there is no tight coupling between presentation and content. You can
usually do this by adding a third-party HTML wrapper package like htmlKona to the
mix. But even this approach, though providing some flexibility with simple screen
changes, still does not shield you from a change in the presentation format itself. For
example, if your presentation changed from HTML to DHTML, you would still need to
ensure that wrapper packages were compliant with the new format. In a worst-case
scenario, if a wrapper package is not available, you may end up hardcoding the
presentation within the dynamic content. So, what is the solution? As you shall soon
see, one approach would be to use both JSP and servlet technologies for building
application systems.

Differing philosophies
The early JSP specifications advocated two philosophical approaches for building
applications using JSP technology. These approaches, termed the JSP Model 1 and
Model 2 architectures, differ essentially in the location at which the bulk of the
request processing was performed. In the Model 1 architecture, shown in Figure 1,
the JSP page alone is responsible for processing the incoming request and replying
back to the client. There is still separation of presentation from content, because all
data access is performed using beans. Although the Model 1 architecture should be
perfectly suitable for simple applications, it may not be desirable for complex
implementations. Indiscriminate usage of this architecture usually leads to a
significant amount of scriptlets or Java code embedded within the JSP page,
especially if there is a significant amount of request processing to be performed.
While this may not seem to be much of a problem for Java developers, it is certainly
an issue if your JSP pages are created and maintained by designers -- which is
usually the norm on large projects. Ultimately, it may even lead to an unclear

© Trendz Information Technologies Ltd. Page No. 212 of 220


J2EE

definition of roles and allocation of responsibilities, causing easily avoidable project-


management headaches.

Figure 1: JSP Model 1 architecture

The Model 2 architecture, shown in Figure 2, is a hybrid approach for serving


dynamic content, since it combines the use of both servlets and JSP. It takes
advantage of the predominant strengths of both technologies, using JSP to generate
the presentation layer and servlets to perform process-intensive tasks. Here, the
servlet acts as the controller and is in charge of the request processing and the
creation of any beans or objects used by the JSP, as well as deciding, depending on
the user's actions, which JSP page to forward the request to. Note particularly that
there is no processing logic within the JSP page itself; it is simply responsible for
retrieving any objects or beans that may have been previously created by the
servlet, and extracting the dynamic content from that servlet for insertion within
static templates. In my opinion, this approach typically results in the cleanest
separation of presentation from content, leading to clear delineation of the roles and
responsibilities of the developers and page designers on your programming team. In
fact, the more complex your application, the greater the benefits of using the Model
2 architecture should be.

Figure 2: JSP Model 2 architecture

© Trendz Information Technologies Ltd. Page No. 213 of 220


J2EE

Session Facade

Context
Enterprise beans encapsulate business logic and business data and expose their
interfaces, and thus the complexity of the distributed services, to the client tier.

Problem
In a multitiered Java 2 Platform, Enterprise Edition (J2EE) application environment,
the following problems arise:

 Tight coupling, which leads to direct dependence between clients and


business objects;
 Too many method invocations between client and server, leading to network
performance problems;
 Lack of a uniform client access strategy, exposing business objects to misuse.

A multitiered J2EE application has numerous server-side objects that are


implemented as enterprise beans. In addition, some other arbitrary objects may
provide services, data, or both. These objects are collectively referred to as business
objects, since they encapsulate business data and business logic.

J2EE applications implement business objects that provide processing services as


session beans. Coarse-grained business objects that represent an object view of
persistent storage and are shared by multiple users are usually implemented as
entity beans.

Application clients need access to business objects to fulfill their responsibilities and
to meet user requirements. Clients can directly interact with these business objects
because they expose their interfaces. When you expose business objects to the
client, the client must understand and be responsible for the business data object
relationships, and must be able to handle business process flow.

However, direct interaction between the client and the business objects leads to tight
coupling between the two, and such tight coupling makes the client directly
dependent on the implementation of the business objects. Direct dependence means
that the client must represent and implement the complex interactions regarding
business object lookups and creations, and must manage the relationships between
the participating business objects as well as understand the responsibility of
transaction demarcation.

As client requirements increase, the complexity of interaction between various


business objects increases. The client grows larger and more complex to fulfill these
requirements. The client becomes very susceptible to changes in the business object
layer; in addition, the client is unnecessarily exposed to the underlying complexity of
the system.

Tight coupling between objects also results when objects manage their relationship
within themselves. Often, it is not clear where the relationship is managed. This
leads to complex relationships between business objects and rigidity in the
application. Such lack of flexibility makes the application less manageable when
changes are required.

© Trendz Information Technologies Ltd. Page No. 214 of 220


J2EE

When accessing the enterprise beans, clients interact with remote objects. Network
performance problems may result if the client directly interacts with all the
participating business objects. When invoking enterprise beans, every client
invocation is potentially a remote method call. Each access to the business object is
relatively fine-grained. As the number of participants increases in a scenario, the
number of such remote method calls increases. As the number of remote method
calls increases, the chattiness between the client and the server-side business
objects increases. This may result in network performance degradation for the
application, because the high volume of remote method calls increases the amount
of interaction across the network layer.

A problem also arises when a client interacts directly with the business objects. Since
the business objects are directly exposed to the clients, there is no unified strategy
for accessing the business objects. Without such a uniform client access strategy, the
business objects are exposed to clients and may reduce consistent usage.

Forces

 Provide a simpler interface to the clients by hiding all the complex interactions
between business components.
 Reduce the number of business objects that are exposed to the client across
the service layer over the network.
 Hide from the client the underlying interactions and interdependencies
between business components. This provides better manageability,
centralization of interactions (responsibility), greater flexibility, and greater
ability to cope with changes.
 Provide a uniform coarse-grained service layer to separate business object
implementation from business service abstraction.
 Avoid exposing the underlying business objects directly to the client to keep
tight coupling between the two tiers to a minimum.

Solution
Use a session bean as a facade to encapsulate the complexity of interactions
between the business objects participating in a workflow. The Session Facade
manages the business objects, and provides a uniform coarse-grained service access
layer to clients.

The Session Facade abstracts the underlying business object interactions and
provides a service layer that exposes only the required interfaces. Thus, it hides from
the client's view the complex interactions between the participants. The Session
Facade manages the interactions between the business data and business service
objects that participate in the workflow, and it encapsulates the business logic
associated with the requirements. Thus, the session bean (representing the Session
Facade) manages the relationships between business objects. The session bean also
manages the life cycle of these participants by creating, locating (looking up),
modifying, and deleting them as required by the workflow. In a complex application,
the Session Facade may delegate this lifestyle management to a separate object. For
example, to manage the lifestyle of participant session and entity beans, the Session
Facade may delegate that work to a Service Locator object

© Trendz Information Technologies Ltd. Page No. 215 of 220


J2EE

It is important to examine the relationship between business objects. Some


relationships between business objects are transient, which means that the
relationship is applicable to only that interaction or scenario. Other relationships may
be more permanent. Transient relationships are best modeled as workflow in a
facade, where the facade manages the relationships between the business objects.
Permanent relationships between two business objects should be studied to
determine which business object (if not both objects) maintains the relationship.

Other Patterns:
The term J2EE is tossed around a lot because it is a generic term that covers many
areas of enterprise and distributed development. The J2EE modules and environment
continue to grow as a rapid pace, and as many of you have come to learn, in recent
years J2EE development has had its trials and tribulations.

For exactly this reason, it is important to take advantage of the most efficient and
effective strategies for new development or the refactoring of existing projects. To
keep up with the new developments, it is imperative that you aren't wasting time
maintaining designs that have poor architecture or code that was poorly written.

This article covers how to use and identify design patterns, specifically for the
presentation tier, in J2EE applications. The interest in design patterns has been
around for a number of years in the software industry. However, interest among
mainstream software developers is a fairly recent development -- and one that's long
overdue, in my opinion. There are a number of reasons for this: it takes a highly
experienced engineer to recognize a pattern; it requires collaboration; and it requires
ongoing refinements. It also requires a sense of fluidity. Design patterns are not
absolutes -- they are more expressions of proven solutions. It is up to you, the
engineer or architect, to apply a pattern appropriately to your given scenario. This, of
course, is easier said than done.

Patterns are not a magic pill. Just because a problem is observed and a pattern
applied does not mean that you will have a perfect application -- or solution, for that
matter. Patterns are a way of bringing clarity to a system architecture and they allow
for the possibility of better systems being built. Building a system that meets the
intended business requirements, performs well, is maintainable, and is delivered on
time, is what keeps us engineers in business. Patterns have the distinct advantage of
helping us do it all quicker.

So, What is a Design Pattern Anyway?


First of all, let's define what a design pattern is. There are a number of formal
definitions, but I prefer a simple approach: a design pattern is simply a description
of a recurring solution to a problem, given a context.

Simple enough. The context is the environment, surroundings, situation, or


interrelated conditions within which the problem exists. Design patterns have a
number of advantages, among which are:

• They capture engineering experience.


• Once described, any level engineer can use the pattern.
• They allow for reuse without having to reinvent the wheel on a project-by-
project basis.
• They allow for us, as engineers, to better define system structure.

© Trendz Information Technologies Ltd. Page No. 216 of 220


J2EE

• They provide a design vocabulary.


• They also provide reusable artifacts.

It is a rare instance (although not an impossible one), that a design pattern is used
in an isolated fashion. Typically, patterns have relationships and work together to
form a weave, in that a pattern can be composed of, or rely on, other patterns. That
is why you will see that the more familiar you are with different patterns, the better
equipped you are to determine their interactions. Patterns can also form frameworks
that can then be used for implementations.

Enterprise systems are built crossing many tiers; this should not be news to anyone
reading this article. This discussion is focused on the presentation tier, and primarily
covers patterns that can be used with JSP and Servlet technology.

Show Me Your Pattern


There are a number of patterns that have been identified by the Sun Java Center for
the presentation tier. These are certainly not the only patterns that can be applied,
but they are a good starting point. The presentation patterns take into consideration
the logic required to service clients that access the system. This includes client
requests, single sign-on, session management, access to business services, as well
as the creation, formatting, and delivery of responses to the client. These patterns
include:

• Intercepting Filter: facilitates preprocessing and post-processing of a


request.
• Front Controller: provides a centralized controller for managing the handling
of requests.
• View Helper: encapsulates logic that is not related to presentation
formatting into Helper components.
• Composite View: creates an aggregate View from atomic subcomponents.
• Service To Worker: combines a Dispatcher component with the Front
Controller and View Helper patterns.
• Dispatch View: combines a Dispatcher component with the Front Controller
and View Helper patterns, deferring many activities to View processing.

While the patterns listed above might not mean anything to you now, by the time
you finish this article, you will understand how using patterns to describe solutions
will give you a clear understanding of the problem being discussed, just by the use of
the pattern name. Think of the time that will be saved in meetings by just saying, "I
think the View Helper can be applied here," instead of droning on about a complete
problem description that we've all faced many times.

Helpful Hints
A couple of words of advice if you are just starting on your pattern odyssey. If you
haven't boned up on UML yet, the time is now. UML is quite commonly used to
describe patterns in pattern catalogs, including class diagrams, sequence or
interaction diagrams, and stereotypes. I'm not going to go into UML in this article,
but I highly recommend getting up to speed on it.

When using patterns, it is important to define a naming convention. It will be much


easier to manage a project as it grows and identify exactly what role an object plays
by using such naming conventions. For example, if you are using the View Helper or

© Trendz Information Technologies Ltd. Page No. 217 of 220


J2EE

Composite View patterns, you might want to define the naming convention to be
[action]Helper.java or [action].jsp, respectively; for instance, CreatePageHelper.java
or CreatePage.jsp.

Make a list of requirement statements that you will be addressing and then try to
identify relevant patterns (once you are familiar with them) that might be applicable.
By doing this, you will be amazed at how quickly you will start to recognize
appropriate solutions to problems. For example:

• Requirement: I need one place of control for handling all requests.


Possible pattern: Front Controller Intercepting Filter.
• Requirement: I need a generic command interface for delegating processing
from a controller to various helper components.
Possible pattern: Front Controller.
• Requirement: I want to make sure data related to my presentation formatting
logic is encapsulated correctly.
Possible pattern: View Helper.
• Requirement: I need to be able to create one View from a number of sub-
Views.
Possible pattern: Composite view.

Intercepting Filter
The Intercepting Filter intercepts incoming requests and outgoing responses, and
applies a filter. Filters may be added and removed in a declarative manner, allowing
them to be applied in a variety of combinations. After pre- or post-processing is
finished, the final filter in the group passes control to the original target object,
typically a Front Controller.

Front Controller
A Front Controller is a container that holds common processing logic that occurs
within the presentation tier and that may otherwise be misplaced in a View. A
controller handles requests and manages content retrieval, security, view
management, navigation, and delegation to a Dispatcher component, which further
dispatches to a View.

View Helper
View Helper encourages the separation of formatting-related code from other
business logic. It suggests using Helper components to encapsulate logic relating to
initiating content retrieval and validation, as well as adapting and formatting the
model. The View component is then left to encapsulate the presentation formatting.
Helper components typically delegate to Business Services (a business-tier pattern
which we won't be discussing further).

Composite View
The Composite View composes a View from numerous pieces. Multiple smaller views,
which could be either static or dynamic, are pieced together to create a single
template.

Service to Worker and Dispatcher View


The Service to Worker and Dispatcher View patterns are a combination of other
patterns. They share a common structure with common components, consisting of a
controller working with a Dispatcher, Views, and Helpers, but the division of labor is
different. The Dispatcher View defers content retrieval and error handling to the time

© Trendz Information Technologies Ltd. Page No. 218 of 220


J2EE

of View processing. This pattern also suggests that the Dispatcher plays a more
limited role in the View management, as the choice of the View is typically already
included in the request.

You may already be using pattern-like strategies in your development process


without even being aware of it. By identifying and becoming familiar with particular
patterns, you will see that what might first appear as a problem that requires a roll-
your-own solution might be better served by using a defined pattern. Let's explore a
sample development scenario using the View Helper pattern and see how this might
work.

Sample Scenario
Our scenario is a system that creates presentation content that requires processing
of dynamic business data. This is a fairly common scenario to anyone doing J2EE
development. The problem is that it is not uncommon for changes to occur in the
presentation tier during the course of development. When business data logic and
presentation-formatting logic are mingled together, the system becomes less flexible,
harder to maintain, and provides a poor separation of tiers. Most of us -- even me --
are guilty at one point or another of coding Java into our JSPs. We want to avoid this
situation.

Enter the View Helper pattern. The solution is to enforce this pattern so that the View
contains formatting code, delegating its processing responsibilities to its helper
classes. These classes might be implemented in a number of ways, including
JavaBeans or custom tags. Helpers also store the View's intermediate data model
and serve as business data adapters. It is important not to confuse a solution with its
implementation strategy. For example, it's possible to implement this type of solution
using a JSP View strategy, which uses a JSP as the View component. While this is a
common strategy, it's also possible to take a Servlet View strategy, which uses a
Servlet as a view instead. While we all know that a JSP actually becomes a Servlet,
the strategy chosen becomes a matter of preference among the teams involved, as
well as of the requirements of your project.

We have identified that we will use the View Helper pattern in our project. The class
diagram for this pattern is shown in Figure 1.

Figure 1.

The class diagram tells us what components we will need to create in order to realize
this pattern. Remember the naming conventions we spoke about earlier! (As a side
note, if you are working with Rational Rose, there are a number of patterns included
in v2002 that allow for the actual classes that need to be realized for a pattern to be

© Trendz Information Technologies Ltd. Page No. 219 of 220


J2EE

generated for you. Depending on the pattern you select, the appropriate classes are
created. It's quite convenient.) By using a sequence diagram that represents the
View Helper pattern, we are quickly able to see the logic flow.

Figure 2.

The View represents and displays information to the client. Dynamic data is required,
and the display is retrieved from a model. The helpers are used to support the View
by encapsulating and adapting the model for displaying. A Helper is called a
ValueBean if it is storing intermediate data from the model needed by the View. How
the helper is implemented doesn't really matter; it could be a JavaBean or a custom
tag, as we previously discussed. It could also be an XSL transformer, if XSL is being
used for converting the model into the appropriate output for a specific client device.
The Business Service is the service the client is trying to access -- the Business
Service would typically branch off into another pattern specific to handling control
and protection of the business service.

By using this pattern, we improve the tier partitioning and maintainability of our
application by using helpers, as well as providing JavaBeans, custom tags, or XSL
files that could very well be reused on other projects. The View Helper is also a good
example of when a pattern is commonly used in conjunction with other patterns.

Take note: this is just the beginning. While this simple sequence diagram is a starting
point, you will have to learn how to adapt your system modelling to your own
development. Transforming patterns and strategies into an implementation is non-
trivial. The more familiar you become with patterns, their strategies, and their
implementation, the quicker you will be able to determine what you need for a
specific project.

© Trendz Information Technologies Ltd. Page No. 220 of 220

You might also like