| CONTENTS | PREV | NEXT | INDEX | J2EE BluePrints |
1. How can I find a particular enterprise bean (component based on EJB technology)?
Enterprise bean instances reside in containers for EJB 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:
This code returns a reference to the home interface,// ... 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) { ... } }
InventoryHome for entity bean
InventoryEJB.
It demonstrates the three steps to finding the home
interface for an enterprise bean:
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.)
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.
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:
MethodInventoryHome 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()); }
findByPrimaryKey() takes a LineItem's
item number as its argument, and returns the Inventory
remote reference as a result.
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?
TRANSACTION_REQUIRED on
everything, including obviously non-transactional, asynchronous
things like sending email. Don't tie up the transaction
pool with spurious transaction requirements.
Address.
Instead, use an Address Value Object that represents
the entire address as a single, synchronizable chunk.
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:
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; }
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.
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:
Serializable, and nontransient,
nonserializable fields cause default serialization to fail.
This particular method of dealing with enterprise bean references
applies only to entity beans.
findByPrimaryKey()
to find the bean's remote interface.
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 1.1 specification (§ 11.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.
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:
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).
For more on value objects, see the Value Object design pattern.
11. How can I prevent concurrent calls to the same stateful session bean?
The EJB 1.1 specification (§ 6.5.6) specifically disallows
multiple simultaneous accesses to the same stateful session bean. The
EJB container throws a RemoteException if a client
accesses a stateful session bean that is already serving a request
from another client. (This is in contrast to entity and stateless
session beans, which have new instances created by the container when
concurrent calls occur.) Note that the concurrent call restriction
implies disallowance of loopback calls on stateful session beans,
as well.
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:
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 J2EE?
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.
|
See also: |
| Session state in the EJB tier |
| EJB Restrictions |