Guidelines, Patterns, and code for end-to-end Java applications.
Questions and Answers - Enterprise JavaBeans Tier
- How can I find a particular enterprise bean (component based on Enterprise JavaBeansTM
technology)?
- Should an enterprise bean look up the Java Database ConnectivityTM (JDBCTM) technology-based drivers directly from
the ejbCreate() method?
- What are some common pitfalls and practices to avoid when using enterprise
beans?
- How can I take advantage of my application server's connection pooling
features?
- How can I hold and pass references to enterprise beans?
- How can nested transactions be implemented with enterprise beans?
- Can I use synchronization primitives in my enterprise beans?
- How do I map a single entity bean to multiple tables?
- What does "stateless" mean in a stateless session bean? Can stateless
session beans cache any information?
- What are enterprise bean "value objects"? How do you persist value
object instances?
- How can I prevent concurrent calls to the same stateful session bean?
- What are the issues involved in creating a singleton with enterprise beans?
- What happens when an enterprise bean throws an exception? How are
exceptions propagated?
- What are some guidelines for using exceptions in J2EETM?
- How should system exceptions be handled from an enterprise bean?
- How do I efficiently remove a bunch of entity beans?
- Does the container-managed persistence (CMP) in the EJB 2.0 specification obviate the need for the Data Access Object (DAO) pattern?
- Where should the container-managed relationship (CMR) fields of an entity bean be set, in the
ejbCreate() method or in the ejbPostCreate() method?
1. How can I find a particular enterprise bean (component based on Enterprise JavaBeansTM technology)?
Enterprise bean instances reside in containers for Enterprise JavaBeans (EJBTM) technology-based
components (EJB containers), within servers enabled with EJB
technology (EJB servers). A client accesses an enterprise bean by
calling methods on the bean's remote interface. A client "finds" a
particular enterprise bean instance by getting a remote reference
(that is, an object that implements the remote interface) to that
bean.
A client finds an enterprise bean by acquiring a reference to
the bean's home interface, and then using "create" or "find"
methods to retrieve a remote reference to the bean.
The sample code below shows how Web tier objects in the Java Pet Store
find an enterprise bean's home interface, and then use it to find a
specific bean instance on the server by getting a remote reference to
the instance. This example uses the home interface "finder" method
findByPrimaryKey()) to find a particular entity bean. The
following code is adapted from the Java Pet Store class
EJBUtil:
// ... class definition ...
public static InventoryHome getInventoryHome() {
try {
// Create a naming service object
InitialContext initial = new InitialContext();
// Obtain an abstract reference to the EJB's home interface.
Object objref = initial.lookup("java:comp/env/ejb/inventory");
// Convert the abstract reference to the home interface.
InventoryHome ih =
(InventoryHome) PortableRemoteObject.narrow(objref,
InventoryHome.class);
return ih;
} catch (NamingException ne) {
...
}
}
|
This code returns a reference to the home interface,
InventoryHome for entity bean
InventoryEJB.
It demonstrates the three steps to finding the home
interface for an enterprise bean:
-
Create a naming service object. Class
InitialContext provides
clients with a single point of access to naming services. An
InitialContext object allows you to find enterprise
beans' home interfaces by name without knowing the details of the
underlying naming service (which could be any of Java Naming and
Directory InterfaceTM (JNDI), DNS, CORBA, COS, etc.)
-
Obtain an abstract reference to the enterprise bean's
home interface. Method
InitialContext.lookup(),
given an object's name, returns a reference to that object,
using whatever directory service is configured. This abstract
can be converted to (but itself is not necessarily) a reference to
the enterprise bean's home interface.
-
Convert the abstract reference to the home
interface. The reference returned by
InitialContext.lookup() (which is always of type
java.lang.Object) may or may not be the actual home
interface object itself, depending upon the Container type. The EJB
1.1 specification chooses Java Remote Method Invocation (Java RMI)
over IIOP as the standard programming model, and CORBA reference types
can't be directly cast to home interfaces. The public static method
PortableRemoteObject.narrow() takes a reference received
from InitialContext.lookup(), and the
java.lang.Class object of the home interface, and tries
to turn the reference into an instance that implements that
interface. If it succeeds, the object returned will implement the home
interface; otherwise, narrow() throws an
exception. Consider narrow() to be performing
type-casting by a single function call.
Once you have the home interface, you can then use a finder method
like findByPrimaryKey() to get the enterprise bean
instance you want. The finder method findByPrimaryKey()
can then be used to access a specific entity bean, as occurs in the
following excerpt from
OrderHandler:
InventoryHome inventHome = EJBUtil.getInventoryHome();
...
for (Iterator it = lineItems.iterator(); it.hasNext();) {
LineItem LI = (LineItem)it.next();
Inventory inventRef = inventHome.findByPrimaryKey(LI.getItemNo());
inventRef.reduceQuantity(LI.getQty());
}
|
Method findByPrimaryKey() takes a LineItem's
item number as its argument, and returns the Inventory
remote reference as a result.
2. Should an enterprise bean look up the Java Database ConnectivityTM (JDBCTM) technology-based drivers directly from
the ejbCreate() method?
No, enterprise beans should always access data sources using the JNDI
interface to get a DataSource object. This allows the
application server to optimize connection management. See the
discussion on connection pooling below for more on this topic.
3. What are some common pitfalls and practices to avoid when using enterprise
beans?
-
Avoid making non-transaction services REQUIRED.
Developers sometimes put
TRANSACTION_REQUIRED on
everything, including obviously non-transactional, asynchronous
things like sending email. Don't tie up the transaction
pool with spurious transaction requirements.
-
Avoid fine-grained objects.
Use the
Transfer Object pattern to group attributes that are always used together. This will help reduce unnecessary
latency. For example, there's seldom a reason to have a remote interface
method that sets and gets the apartment number of an
Address.
Instead, use an Address Transfer Object that represents
the entire address as a single, synchronizable chunk.
-
Don't embed the SQL code within the enterprise bean code.
Enterprise beans using a relational data store with
bean-managed persistence (BMP)
often use SQL to manipulate bean instance state. Placing
the SQL directly within the enterprise bean code can make the bean
dependent on a particular vendor's SQL implementation. Instead of
using SQL directly in enterprise bean methods, use Data
Access Object class to abstract persistence operations
(see
Data Access Objects).
-
Avoid unnecessary remote calls.
Remote calls are expensive in network bandwidth, server load, and
latency. Always be sure that every server round-trip is absolutely
necessary. In particular, try to eliminate iterating
server-side collections; instead, try to create a generalized
business method that does whatever that iteration is trying to
accomplish -- even if it's just positioning a cursor.
For example, instead of doing this:
for (i = 0, remoteObj.first(); i < firstItem && remoteObj.valid(); i++) {
remoteObj.next();
}
try this:
remoteObj.seek(firstItem);
The iteration's still there, it's just within the seek()
method on the server, instead of being on the client.
4. How can I take advantage of my application server's connection pooling
features?
Most of the techniques for making most effective use of your
EJB server's connection pooling feature involve properly managing
the lifecycle of your enterprise beans and the connections they use. Here
are some guidelines and explanations:
-
Avoid static connection fields.
Enterprise bean tutorial code samples often acquire (at creation time)
a reference to a connection, and save that connection in a protected
static class field, for use during subsequent calls. This technique
starves the connection pool, since the enterprise bean class is holding on to a
connection even when no methods are being called on the
bean. Therefore, always acquire a connection, use it, and return the
connection to the pool before returning from the method.
-
Acquire connections through a DataSource, instead of using
the JDBC interface.
An enterprise bean server provider implements connection pooling
behind a reference to an object that implements
DataSource. Using the JDBC
interface directly to acquire database connections competes with the
connection pool for available connections. Below you will find sample
code from the Java Pet Store that gets a (potentially pooled)
connection from a DataSource. The Java Pet Store "DAO"
(Data Access Object) classes use this technique.
// utility method
private Connection getDBConnection() throws SQLException {
Connection connection;
try {
// Get the naming service
InitialContext ic = new InitialContext();
// Find the data source
DataSource ds = (DataSource)
ic.lookup(JNDINames.ESTORE_DATASOURCE);
// Get the connection
connection = ds.getConnection();
} catch (NamingException ne) {
throw new EJBException(ne);
} catch (SQLException se) {
throw new EJBException(se);
}
return connection;
}
|
-
Always acquire, use, and release a connection within
a method
Within any method that uses connections, always obtain a connection
from a
DataSource (as described above), use it, and then
release it. Following this guideline ensures that connections are only
held as long as they are used. The time penalty for acquiring the
connection is low, because the connection already exists and is
open and waiting in the pool. Pay special attention to releasing
the Connection object if you use early returns (multiple
returns from a method), or if exceptions are involved.
Using the try/catch/finally construct is a good
way to ensure that connections are always released properly. The
finally clause executes unconditionally
whenever the flow control exits the try block, either by completing
successfully or if an exception is thrown, even for exceptions that are
not caught. Here's an excerpt from the Java Pet Store class
AccountDAO that demonstrates this idea:
private boolean userExists (String userId)
throws AccountDAOException {
...
try {
getDBConnection();
stmt = dbConnection.createStatement();
result = stmt.executeQuery(queryStr);
... (use the result set) ...
} catch(SQLException se) {
...
} finally {
if (result != null) { closeResultSet(result); }
if (stmt != null) { closeStatement(stmt); }
if (dbConnection != null) { closeConnection(); }
}
return returnValue;
}
|
Inside the try block above, the getDBConnection()
method initializes the protected field dbConnection,
which is used to create a statement, get a result set, and so on.
The catch clause handles all SQLExceptions
thrown, but no other kinds of exceptions. Nevertheless, the
finally clause closes the database connection
regardless of how the try block was exited.
Note that some databases react badly if connections are closed while
they have open statements or result sets, so it's good form to always
close any outstanding statement and its result set before closing the
corresponding connection.
Following these guidelines will help your application to make
most effective use of the application server's connection
pooling mechanism.
5. How can I hold and pass references to enterprise beans?
The EJB 1.1 specification does not prescribe a way for entity beans to
store references to other entity beans. Application developers may
choose from among several ways that entity beans can reference one
another. These strategies include:
-
Store the remote reference in a transient, protected field
of an entity bean.
Entity beans can retain references to other entity beans in
instance fields. The field should be protected, as all internal
state should be; and it should be transient, because remote
references aren't
Serializable, and nontransient,
nonserializable fields cause default serialization to fail.
This particular method of dealing with enterprise bean references
applies only to entity beans.
-
Use the referenced bean's primary key.
If the referenced bean's home interface is known and fixed,
the referencing bean can use get the home interface of the
referenced bean, and then use
findByPrimaryKey()
to find the bean's remote interface.
-
Use the bean's handle.
See
Handle. This handle is
a direct reference to the referenced bean. The referencing bean
can instantiate the Handle, and call the resulting
object's method getEJBObject() to retrieve the
referenced bean's remote interface.
Every remote reference (that is, every instance of a subclass of
EJBObject)
has a method getHandle() that returns a serializable
Handle. A handle is a serializable object, created by the
application server, that specifies how to find a particular enterprise
bean. The Handle object has a method called
getEJBObject() which will return the referenced object if
that object exists and can be found.
Handles are typically used as robust references to server-side
objects. Since they are implemented by the server vendor, their
implementations are opaque; that is, there's no specified format for
what should be in a Handle object. But since handles are
Serializable, they can be turned into a byte string and
stored or transmitted through a network, by way of Java RMI or any
other protocol. Cooperating clients or server-side objects can pass
handles to one another as byte strings. The receiving program can
instantiate a received Handle, and then request its
EJBObject.
There is no guarantee that the bean will in fact exist when its handle
is referenced. Session beans aren't guaranteed to survive container
crashes, and the data representing an entity bean can be removed while
serialized handles are outstanding. The Handle does
guarantee either to return a reference to the remote interface for a
bean, or to throw an exception if the bean couldn't be located.
6. How can nested transactions be implemented with enterprise beans?
The EJB 2.0 specification (§ 17.1.2) states that nested
transactions are not supported. Requiring nested transactions would
have shut out the many database vendors who do not support them.
The specification leaves open the possibility adding
nested transactions in the future.
7. Can I use synchronization primitives in my enterprise beans?
Synchronization primitives include the synchronized
keyword, as well as methods wait(),
notify() and notifyAll() of class
Object. All of these are disallowed in enterprise beans, as
are other methods (such as Thread.interrupt(), etc.) that
would affect the server thread pool.
Since the EJB server is completely responsible for
managing threads and synchronization, there is absolutely no reason to
use synchronization primitives in your enterprise beans.
While Vector and other classes that contain synchronized
blocks is allowed in general, we recommend using the standard Java
Collection classes (which are not synchronized) instead.
Restricting synchronization and thread control guards against the
possiblity of deadlock. Enterprise bean instance access is
synchronized automatically by the server at specific points in an
enterprise bean's lifecycle. Allowing arbitrary synchronization
control in enterprise beans could cause deadlock.
For more on restrictions placed on enterprise beans, see
EJB Restrictions.
8. How do I map a single entity bean to multiple tables?
If you use Bean-Managed Persistence (BMP), map the bean to the the tables manually. Consider
applying the
Data Access Object (DAO) design pattern to accomplish this.
If you're using Container-Managed Persistence (CMP), use the vendors
object/relational mapping tool to specify the mapping between your
object state and the persistence schema. Be aware that the CMP
solution is not portable across application server vendors under the
EJB 1.1 specification. If this is a concern, consider using BMP, and
migrating to CMP under the EJB 2.0 specification or later.
9. What does "stateless" mean in a stateless session bean? Can stateless
session beans cache any information?
Stateless for a stateless session bean means that
the bean can maintain no client-specific state. Stateless
session beans can (and often do) have other state which is
client-independent.
Client-independent data can be cached in a stateless bean if:
- the information being cached will never change, and
- the application can tolerate a copy of the same cache repeated
in each stateless bean instance that the container creates.
In most cases, any client-independent state that a stateless session bean
may have will probably be indentical in all instances.
10. What are enterprise bean "value objects"? How do you persist value
object instances?
Value objects are objects that only have meaning within the
context of another business object. They typically represent fairly
fine-grained business concepts, like an address, credit card details
and so on.
To persist an instance of a value class (which is a regular Java
class, not an enterprise bean), use the class as a single property of
an enterprise bean. For example, an Address class may be
a value class used by class Person (so, in the example,
class Person will have a method Address
getAddress()). The Address may then be persisted
as a whole using CMP. If BMP is used, you'll have to create
persistence management code for class Address that
manages moving Address data to and from the appropriate
table(s).
11. How can I prevent concurrent calls to the same stateful session bean?
The EJB 2.0 specification (§ 7.5.6) specifically disallows
multiple simultaneous accesses to the same stateful session bean.
If a client-invoked business method is in progress on an instance
when another client-invoked call, from the same or a different client,
arrives at the same instance, the container may throw RemoteException
to the second client, if the client is a remote client, or EJBException
if the client is a local client. This is in contrast to entity and stateless
session beans, which have new instances created by the container when
concurrent calls occur. In certain special circumstances (for example,
to handle clustered web container architectures), the container may
instead queue or serialize such concurrent requests. However, the clients
can not rely on this behavior. Note that this concurrent call restriction
forbids loopback calls on stateful session beans as well.
Since stateful session beans correspond one-to-one with a user,
multiple concurrent calls to a single stateful session bean instance
is nonsensical. Nevertheless, there are several common cases where
it may happen, for example:
-
in Java Foundation Classes/Swing GUI code where multiple threads may
be contending for access to the same bean;
- in Web applications where multiple frames may be trying to
access the same bean;
- if a user
double-clicks a hyperlink that makes a call against an enterprise bean; or
-
the user opens more than one browser instance and is interacting
through both.
Access to a stateful session bean can be serialized by creating a
Web-tier class that acts as a proxy to a stateful session bean in the
EJB tier. The proxy class would present have the same interface as the
session enterprise bean, and would forward all calls to the session
enterprise bean, but its methods would be synchronized. Keep in mind
that other clients must be prevented from accessing the session
enterprise bean remote interface directly.
The Java Pet Store sample application uses this technique in class
ShoppingClientControllerWebImpl. Notice that the
synchronized methods all defer their calls to another
enterprise bean. This effectively serializes all concurrent calls from
the Web tier to the bean in the EJB tier.
12. What are the issues involved in creating a singleton with enterprise beans?
In general, the meaning of "singleton" in a distributed context is not
entirely clear, particularly in the context of multiple Java virtual
machines, clustered servers, and/or failover.
A network-wide singleton can be created as an Java RMI
technology-enabled object and accessed through JNDI. Such a service,
however, could become a single point of failure in the system: lose
that service, and your entire distributed application could fail.
Also, the J2EE 1.1 specification doesn't require that the server
support an Java RMI registry, so the application will necessarily be
portable across application servers.
Another option is to use an entity bean with a single primary key and
store all state in a (potentially distributed) database. Note that
containers may create multiple instances to serve concurrent requests
for the primary key, so all state must be kept in a database. This
solution could therefore easily become a performance bottleneck, as
multiple clients wait for access to the same singleton object. This,
however, is a concern with the general notion of "singleton" in a
distributed environment (access to a singleton is serial, atomic
access to a single object instance), not a limitation of enterprise
bean technology.
13. What happens when an enterprise bean throws an exception? How are
exceptions propagated?
The EJB 1.1 specification (Chapter 12) defines two types of exceptions:
-
application exceptions, which indicate recoverable
errors at the level of the application (for example, bad user input); and
-
system exceptions, corresponding to generally
unrecoverable, unexpected errors below the application level (for
example, loss of database connection, Java virtual machine errors,
unexpected
RuntimeException), etc.
Application exceptions thrown by the enterprise bean layer are
propagated to the client by the EJB container. All other enterprise
bean exceptions, either RuntimeExceptions or subclasses
of java.lang.Exception, are caught and handled by the
container, which then throws a RemoteException to the
calling client.
Enterprise beans are responsible for catching system exceptions and
converting them to EJBExceptions.
See Chapter 12 of the EJB 1.1 specification for more on this topic.
Pay particular attention to tables 8 and 9, which each present a
matrix describing the method conditions and specified responses
for various exception conditions and transaction demarcations.
14. What are some guidelines for using exceptions in J2EETM?
Application exceptions should be exclusively subclasses of
Exception. Exceptions should report
only exceptional application conditions from which the client can
recover, and should not report system-level problems. Application
exceptions do not as a rule cause client transaction rollback or
server-side bean destruction. If rollback is desired when an exception
occurs, catch the exception, and call
EJBContext.setRollbackOnly() to mark the
transaction (irrevocably) for rollback. The container will perform the
rollback on behalf of the bean. Otherwise, clients can catch
application exceptions and continue to communicate with the enterprise
bean, with the transaction context intact. This method only works for
beans with container-managed transactions.
The EJB 1.1 specification, § 9.2.6, contains a compatibility
note pointing out that the EJB 1.0 technology practice of allowing
business methods to throw non-application-level
java.rmi.RemoteExceptions is now deprecated. Throw and
EJBException or a RuntimeException to notify
the container of non-application error conditions.
15. How should system exceptions be handled from an enterprise bean?
All system exceptions must be caught by the bean and propagated
to the container as
EJBExceptions. RuntimeExceptions are the
exception to this rule, and can be allowed to propagate to the
Container without first being caught.
16. How do I efficiently remove a bunch of entity beans?
If you are using container-managed persistence, you can write a home business method that removes all the entity beans. If you are using bean-managed persistence, you can write a single JDBC call that removes the underlying table rows. However, you will need to use proper isolation level settings for the database to avoid problems such as phantom reads.
17. Does the container-managed persistence (CMP) in the EJB 2.0 specification obviate the need for the Data Access Object (DAO) pattern?
No. There may be scenarios where the query Language capabilities
of the EJB 2.0 specification may not be enough for your application. In such cases, you may need to resort to using SQL code encapsulated in a DAO. Furthermore, if you are using mostly read-only data primarily in a tabular form, it may be more natural and efficient to access it relationally through a DAO. In such situations, using entity beans may incur a performance
penalty while providing little or no additional value. The catalog component of the Java Pet Store demo uses DAO for this reason.
18. Where should the container-managed relationship (CMR) fields of an entity bean be set, in the ejbCreate() method or in the ejbPostCreate() method ?
In the ejbPostCreate() method. This is because the container creates the primary key of the CMP entity bean only after the ejbCreate() returns. Since, the primary key is needed to establish the container-managed relationships, the CMR fields must be set in the ejbPostCreate() method. See CustomerEJB in the Java Pet Store Demo, version 1.3.1 for an example.
|
|