Professional Documents
Culture Documents
Disclaimer: This document is my notes taken from the book Mastering EJB 3rd Edn. by Ed Roman et al and
Head First EJB by Kathy Sierra and Bert Bates. This document covers more than the syllabus for the
SCBDC exam and is well suited for revising the concepts of EJB 2.0 and 2.1.
Introduction
EJB is about rapid application development for the server side; you can quickly
and easily construct server-side components in Java by leveraging a prewritten
distributed infrastructure provided by the industry. EJB is designed to support
application portability and reusability across any vendor’s enterprise
middleware services.
SOA is a paradigm. There are many possible ways of building software so that it
implements salient features of SOA, mainly coarse granularity and loose coupling.
One such way is Web services.
EJB Ecosystem
Bean Provider (Internal department Developers writing the EJBs.
providing EJBs to other departments
EJB Deployer (a staff person, a Securing deployement within h/w or s/w firewall.
vendor or consultant). Integrating with enterprise security and policy
repositories, which oftentimes is an LDAP server
Choosing h/w to host the application.
Providing redundant h/w for clustering.
Performance tuning the system (by modifying the
deployement descriptors).
System Administrator Oversee the stability of operational system.
Upkeep and monitoring of deployed system making use
of management tools that the EJB server provides (JMX
console of JBoss) or Weblogic AS provides SNMP
manageability.
Server Provider (BEA, IBM, Provides EJB container (Application Server).
JBoss(now Redhat)).
Tools Vendor (BEA Weblogic Provide IDEs to build/debug EJBs.
Workshop, IBM Websphere
Application Studio, Eclipse, Netbeans,
IntelliJ IDEA).
Bean provider, application assembler and deployer could be the same person for small companies.
J2EE Technologies
EJB 2.1 Defines how server side components are written and provides a
standard contract between components and application server
(container).
JAX-RPC Support for developing Webservices. Defines two webservices
endpoint models: one based on servlet and other based on
stateless session EJB.
RMI-IIOP Extension of RMI for CORBA integration.
JNDI Used to access naming and directory systems such as Microsoft
Exchange or Lotus Notes.
JTA & JTS Allow for components to be bolstered with transaction support.
JMS Allows for J2EE deployement to communicate using messaging
within and outside the J2EE system. You can connect to existing
MOM (message oriented middleware – IBM MQSeries or MS
MQ). Messaging is an alternative to RMI-IIOP.
JCA Enable J2EE systems to access existing EIS such as mainframe
systems running high-end transactions (like IBM CICS, BEA
TUXEDO), ERP systems or prorietary systems. ISVs such as
SAP, Siebel, PeopleSoft (now Oracle) who want their software to
be accessible from within J2EE application servers can write
standard J2EE connectors which will act similar to JDBC service
provider jars.
JAXP API for parsing XML documents. Is implementation neutral
interface to XMP parsing technologies such as DOM and SAX.
So you can plugin a DOM/SAX parser implementation like
Xerces for Java and use Xerces from JAXP APIs.
Java RMI-IIOP
RMI-IIOP is special version of RMI that is compliant with CORBA and uses both java.rmi and javax.rmi.
RMI-IIOP lacks some features present in RMI like:
Distributed garbage collection.
Object activation.
Downloadable class files.
RMI-IIOP supports both pass-by-reference and pass-by-value models. Following are the steps to build an
RMI-IIOP application:
1. When using RMI-IIOP you must build a custom interface called Remote Interface which extends
java.rmi.Remote.
import java.rmi.Remote;
import java.rmi.RemoteException;
/**
* The remote interface for the remote object. Clients use this
* remote interface to perform any operations on the remote object.
*/
public interface IPKGenerator extends Remote {
public long generate() throws RemoteException;
}
2. To make your object available as a remote object and allow remote hosts to invoke its methods,
your remote class must either
a. extend the javax.rmi.PortableRemoteObject class or
b. in case your class already extends some other class then you can manually export your
object so that it is available to be invoked on by remote hosts by calling
javax.rmi.PortalRemoteObject.exportObject().
import java.rmi.RemoteException;
import javax.rmi.PortableRemoteObject;
/**
* The remote object which generates primary keys
*/
public class PKGenerator extends PortableRemoteObject implements IPKGenerator {
/*
* Our remote object’s constructor
*/
public PKGenerator() throws Exception, RemoteException {
/*
* Since we extend PortableRemoteObject, the super
* class will export our remote object here.
*/
super();
}
/*
* Generates a unique primary key
*/
public synchronized long generate() throws RemoteException {
return i++;
}
private static long i = System.currentTimeMillis();
}
The remote object can be made unavailable for remote method invocation by calling
javax.rmi.PortableRemoteObject.unexportObject().
2. rmic compiler can be used to generate the stub and skeleton which are proxy’s responsible for
delegating the calls made to remote methods to their implementations.
3. Passing parameters by value for object type requires the object type to be serializable.
4. Passing object type parameters by reference is achieved when parameter object is itself a remote
object. So when the client makes a remote method call on the server and passes a remote object
reference of a remote object living on the client machine then the stub to that remote object
reference is serialized (and not the whole object). So the server can connect to the remote object
living on client machine and invoke methods on that. Java RMI-IIOP thus simulates the pass-by-
reference by passing serializable stub, rather than serializing the original object. So, by making
your parameters remote objects, you can effectively avoid the network lag in passing large objects.
1. All Java primitives are passed by value when calling methods remotely.
2. If you want to pass an object by value, it must implement java.lang.Serializable interface.
Anything referenced from within the object must also be serializable.
3. If you want to pass an object by reference, it must be a remote object, and it must
implement java.rmi.Remote interface. A stub for the remote object is serialized and
passed to the remote host.
A way to publish the server and have client locate the server is called bootstrapping and is
achieved by JNDI.
JNDI
JNDI provides standard inteface for locating users, machines, networks, objects and services by name.
Example, locating a printer on the network by name, locating java object or locating a datasource to
connect to a database. In J2EE the uses of JNDI are:
1. acquire a reference to the Java Transaction API (JTA) UserTransaction interface
2. connect to resource factories such as, JDBC drivers or JMS drivers.
3. for beans to lookup other beans.
Naming service:
Associates names with objects (binding names to objects).
Provides a facility to find an object by name (resolving a name to an object reference).
Example, filesystem (file name to file handle), DNS (machine name to IP address).
A directory object is a special type of object stored with the naming service which can be used to store
attributes. Eg. You can use a directory object to represent a user storing information about user, like user
loginid, password, email, phone etc as its attributes. So your application can lookup the user credentials
stored in the directory object of the naming service to authenticate the user.
A directory service is a naming service enhanced to provide directory object operations for manipulating
attributes. Eg, (MS Active Directory Service, Netscape Directory Server).
Directory is a system of directory objects that are all connected in a hierarchical tree structure with a root
representing your company as shown below:
Most directories are implemented by a database behind the scene to store the hierarchical directory objects.
There are several directory protocols for accessing a directory service eg. LDAP (Light weight directory
access protocol) or Novell’s Network Directory System (NDS) or Network Information System (NIS).
IBM’s Lotus Notes and Microsoft Active Directory are both LDAP based. But not all directory services are
LDAP based. JNDI is a bridge over naming and directory services providing common interface to disparate
directories.
Like the database vendors provide JDBC drivers, directory service vendors provide JNDI service
providers to access their specific directories. The providers are aware of specific directory protocols and
they plugin to the JNDI SPI.
LDAP service provider would know how to map JNDI client API to an LDAP operation. Service providers
exist for LDAP, NIS, NDS, SLP, CORBA NS, File System, RMI-IIOP tnameserv, and many more.
To acquire an initial context in JNDI, we use an initial context factory (implemented by JNDI service
provider) which returns the initial context for the naming system. To get an initial context for a certain
naming system you need to provide the following information about the naming system:
o IP address of the naming system
o Port number at which the naming service is listening
o Starting location within the JNDI tree
o User name and passwd for authenticating to the naming service (if applicable).
Note: Naming graphs needed not be trees. They may have more than one root. Subcontexts can be bound in
more than one context, so a single subcontext may be known under more than one name.
EJB Fundamentals
Session Beans Model business processes.
Entity Beans Model business data.
Message driven Beans Can be called only implicitly by sending messages to those beans. Eg. Stock
trade messages, credit card authorization messages, workflow messages.
Middleware
When distributed system grows larger, we need to employ middleware services for transaction, security,
persistence, etc.
Explicit Middleware: involves purchasing a middleware off the shelf and writing code that calls the
middleware’s API. More like Bean managed transaction/persistence/security. Business logic gets intwined
with the logic to call these middleware APIs. Example of such systems are: traditional CICS or TUXEDO
based mainframe systems, CORBA, DCOM or RMI.
Implicit/Declarative Middleware: New component based technologies like EJB, .NET and CORBA
Component Model enable you to harness the complex middleware in enterprise applications without
writing to the middleware APIs. More like Container Managed transaction/persistence/security. You just do
the following:
1. Write distributed object business logic only.
2. Declare middleware services that your distributed object needs in separate descriptor files.
3. The application server will generate the request interceptor from the configuration obtained from
the descriptor files.
4. Request interceptor will intercept requests from client, perform middleware services that your
distributed object needs and then delegates the call to the distributed object.
EJB Object
EJB is not full fledged remote object. Clients never invoke the business method directly on actual bean
instance. Rather, the EJB container intercepts the request, performs the implicit middleware and delegates
to the bean instance. EJB container handles networking for you by wrapping your bean in a network
enabled object. Middleware services at the point of interception include:
1. Transaction service
2. Security service
3. Resource management: for resources like threads, database connections etc.
4. Lifecycle management of the beans: pooling the beans, activating/passivating etc.
5. Persistence service
6. Handle concurrent requests – either by allowing serial access to the bean instance or allowing
other bean instances in the pool to service the request.
Thus, EJB container acts as a layer of indirection between client and the bean by providing a network
aware wrapper object called EJB Object. EJB Object is the request interceptor. They have container
specific code inside them (each container handles middleware differently). EJB Object class file is
generated automatically by the container.
public interface javax.ejb.EJBObject extends java.rmi.Remote
{
public javax.ejb.EJBHome getEJBHome() throws java.rmi.RemoteException;
public java.lang.Object getPrimaryKey() throws java.rmi.RemoteException;
public void remove() throws java.rmi.RemoteException, javax.ejb.RemoveException;
public javax.ejb.Handle getHandle() throws java.rmi.RemoteException;
public boolean isIdentical(javax.ejb.EJBObject) throws java.rmi.RemoteException;
}
Remote Interface
For the container to know what business methods have to made remote you will need to provide a Remote
interface which mentions all business methods that bean class exposes. Your remote interface extends the
javax.ejb.EJBObject interface mentioning all business methods of your bean. When a client invokes any of
these business methods, the EJBObject delegates the method to its corresponding implementation, which
resides in the bean itself.
So the container provided EJBObject concrete implementation implements the Remote interface we create
and provides an implementation for the methods in the javax.ejb.EJBObject interface plus implementation
of the business methods which simply call the business method implementation in your bean class.
java.rmi.Remote
extended by javax.ejb.EJBObject
extended by <YourBean>Remote
Home object is generated by the container and implements the Home interface where you provide
information to the container about how your bean instance has to be initialized while instantiating. Even
instantiation of the bean will be done via the EJB object instance.
java.rmi.Remote
extended by javax.ejb.EJBHome
extended by <Your Bean>Home
Note: Most containers will have a 1:N relationship between home objects and bean instances. This
means that all clients use the same home object instance to create EJB objects. So a home object will be
implemented by container to be thread safe code as it has to service multiple clients. Your beans will
always be single threaded (ie only one thread at a time will execute methods of a certain bean instance).
Relationship between EJB objects and bean instances may be 1:N (as home objects) or M:N. In the
former case, the EJB object will be single and so will be thread safe. In the later case, there will be as many
EJB objects as there are active client connections (so EJB objects will be single threaded).
Local Interface
Local objects implement local interfaces. Avoids the networking overhead.
public interface javax.ejb.EJBLocalObject {
public javax.ejb.EJBLocalHome getEJBLocalHome() throws javax.ejb.EJBException;
public Object getPrimaryKey() throws javax.ejb.EJBException;
public boolean isIdentical(javax.ejb.EJBLocalObject) throws javax.ejb.EJBException;
public void remove() throws javax.ejb.RemoveException, javax.ejb.EJBException;
}
public interface javax.ejb.EJBLocalHome {
public void remove(java.lang.Object) throws javax.ejb.RemoveException, javax.ejb.EJBException;
}
javax.ejb.EJBLocalObject
extended by <Your Bean>Local
and
javax.ejb.EJBLocalHome
extended by <Your Bean>LocalHome
Note: Local interface always marshall parameters by reference.
Deployement Descriptor
This is the key to implicit middleware. It is used to inform the container about your middleware needs,
you as a bean provider must declare your components’ middleware service requirements in this file.
Bean Management and How container should manage your beans.
lifecycle requirements o name of bean class
o type of bean (Session, Entity or MDB)
o home interface that generates the beans
o local or remote
Persistence requirements Whether bean or container managed persistence.
(Entity beans only)
Transaction requirements Requirements for running in transactions
Transaction must start whenever anyone calls a bean (Required New)
Transaction may start whenever there is no running transaction (Required) etc
Security requirements Access control entries
Who is allowed to use which beans
Who is allowed to use each method on a particular bean
Vendor Specific files can be used to configure load balancing, clustering, monitoring, pool sizes etc.
EJB-jar file = bean classes + home interfaces + remote interfaces + deployement descriptor.
Session Beans
Session beans are business process objects that implement business logic and workflows. Eg. Price quoting,
order entry, video compression, banking transactions, stock tradesm database operations, complex
calculations etc.
Lifecycle
Lifetime is equivalent of a client session. Session bean instances are not shared between clients. EJB
container may destroy the session bean if client times out. Session beans are non persistent (not saved to
database).
Lifecycle of stateful session bean is similar to that of stateless session bean, except:
1. there is no pool of equivalent instances because each instance contains state.
2. there are transitions for passivating and activating conversational state.
To limit the number of stateful session bean instances in memory, the container can swap out a stateful
bean, saving its conversational state to a hard disk (passivation). At passivation time the container uses
object serialization to save state of bean (javax.ejb.EnterpriseBean interface extends java.io.Serializable
interface – so every enterprise bean implements this interface). When the original client invokes a method,
the passivated conversational state is swapped in to a bean (activation) which then resumes conversation
with the original client. The bean that receives the activated state may not be the original bean instance that
was passivated. Containers may choose to passivate a bean that has been used least recently (LRU).
Passivation can occur anytime if the bean is not involved in a method call or is not a part of a transaction.
Beans are activated on-demand (as client request comes in). Thus, using passivation/activation, the EJB
container provides effect of instance pooling stateful session beans.
ejbPassivate() – relinquish held resources like database connections, open sockets, open files.
ejbActivate() – restore the open resources it released during ejbPassivate().
Note: in most cases you leave these methods empty. But you may implement them if you do have transient
resources which you donot want to save like socket connections, open files, database connections etc which
you have stored in private member instance variables but that does not make sense to serialize everytime
with the object state.
Note:
1. Stateless session beans can contain state that is not specific to any one client, such as database
connection factory that all clients would use.
2. Any stateless bean can service any client request because they are all exactly same. So stateless
session beans can be pooled and reused. Passivation/activation are not required as there isnt any
state to save.
3. Stateless session beans can also be webservice endpoints.
Remote Interface:
/**
* This is the HelloBean remote interface.
*
* This interface is what clients operate on when
* they interact with EJB objects. The container
* vendor will implement this interface; the
* implemented object is the EJB object, which
* delegates invocations to the actual bean.
*/
public interface Hello extends javax.ejb.EJBObject
{
/**
* The one method - hello - returns a greeting to the client.
*/
public String hello() throws java.rmi.RemoteException;
}
Local Interface:
/**
* This is the HelloBean local interface.
*
* This interface is what local clients operate
* on when they interact with EJB local objects.
Home Interface:
/**
* This is the home interface for HelloBean. This interface
* is implemented by the EJB Server’s tools - the
* implemented object is called the Home Object, and serves
* as a factory for EJB Objects.
*
* One create() method is in this Home Interface, which
* corresponds to the ejbCreate() method in HelloBean.
*/
public interface HelloHome extends javax.ejb.EJBHome
{
/*
* This method creates the EJB Object.
*
* @return The newly created EJB Object.
*/
Hello create() throws java.rmi.RemoteException, javax.ejb.CreateException;
}
create() is the factory method that clients use to get a reference to the EJB Object and it initializes the bean.
Since a remove() method is already there in the EJBHome interface so we donot need to provide that in our
home interface.
Note: create() and remove() are for creation and destruction of an EJB object. The bean instance may not
be destroyed on a remove() or may not be created on create() (rather be dequed from the pool on create()
and returned to the instance pool on remove).
In summary,
1. Application level exceptions are always thrown back to client. This includes any exception that
bean defines and javax.ejb.CreateException or javax.ejb.FindException.
2. For system level exceptions, EJB container can handle the exception and
//
// EJB-required methods
//
public void ejbCreate() {
System.out.println(“ejbCreate()”);
}
public void ejbRemove() {
System.out.println(“ejbRemove()”);
}
public void ejbActivate() {
System.out.println(“ejbActivate()”);
}
public void ejbPassivate() {
System.out.println(“ejbPassivate()”);
}
public void setSessionContext(javax.ejb.SessionContext ctx) {
this.ctx = ctx;
}
//
// Business methods
//
public String hello() {
System.out.println(“hello()”);
return “Hello, World!”;
}
}
EJB Context
Inside the bean you may want to access the security credential of the user currently calling your bean’s
method. Container holds all this information in an EJB Context object. So a context represents a way for a
bean to perform callbacks to the container. EJB Context encapsulates the bean’s domain. The container is
responsible for changing the context to reflect any status change, such as bean being involved in a new
transaction. Context object state will dynamically change over time. The container associates your bean
with a context by calling your setSessionContext() (or setEntityContext(), setMessageDrivenContext()).
When you define each of these methods, you should store the context away in a member variable so that
the context can be queried later.
public interface javax.ejb.EJBContext
{
/*
* Call these from within your bean to access
* your own home object or local home object.
*
* You can use them to create, destroy, or
* find EJB objects and EJB local objects
* of your own bean class type.
*/
public javax.ejb.EJBHome getEJBHome();
public javax.ejb.EJBLocalHome getEJBLocalHome();
/*
* These are transaction methods
*/
public boolean getRollbackOnly();
public void setRollbackOnly();
public javax.transaction.UserTransaction getUserTransaction();
/*
* These are security methods
*/
public boolean isCallerInRole(java.lang.String);
public java.security.Principal getCallerPrincipal();
}
Deployement Descriptor
ejb-jar.xml – standard ejb deployement descriptor.
<?xml version = "1.0" encoding = "UTF-8"?>
<!--
Copyright 2004 Sun Microsystems, Inc. All rights reserved.
SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
-->
<ejb-jar xmlns = "http://java.sun.com/xml/ns/j2ee" version = "2.1" xmlns:xsi =
"http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation = "http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/ejb-jar_2_1.xsd">
<display-name>HelloWorld</display-name>
<enterprise-beans>
<session>
<display-name>HelloWorld</display-name>
<ejb-name>HelloWorld</ejb-name>
<home>examples.HelloHome</home>
<remote>examples.Hello</remote>
<ejb-class>examples.HelloBean</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type>
<security-identity>
<use-caller-identity/>
</security-identity>
</session>
</enterprise-beans>
</ejb-jar>
<sun-ejb-jar>
<enterprise-beans>
<unique-id>1</unique-id>
<ejb>
<ejb-name>HelloWorld</ejb-name>
<jndi-name>HelloHome</jndi-name>
</ejb>
</enterprise-beans>
</sun-ejb-jar>
Client code
import javax.naming.Context;
import javax.naming.InitialContext;
import java.util.Properties;
/**
* This class is an example of client code that invokes
* methods on a simple stateless session bean.
*/
public class HelloClient {
public static void main(String[] args) throws Exception
{
/*
* Setup properties for JNDI initialization.
*
* These properties will be read-in from
* the command-line.
*/
Properties props = System.getProperties();
/*
* Obtain the JNDI initial context.
*
* The initial context is a starting point for
* connecting to a JNDI tree. We choose our JNDI
* driver, the network location of the server, etc.
* by passing in the environment properties.
*/
Context ctx = new InitialContext(props);
/*
* Get a reference to the home object - the
* factory for Hello EJB Objects
*/
Object obj = ctx.lookup(“HelloHome”);
/*
* Home objects are RMI-IIOP objects, and so
* they must be cast into RMI-IIOP objects
* using a special RMI-IIOP cast.
*
*/
HelloHome home = (HelloHome) javax.rmi.PortableRemoteObject.narrow( obj,
HelloHome.class);
/*
* Use the factory to create the Hello EJB Object
*/
Hello hello = home.create();
/*
* Call the hello() method on the EJB object. The
* EJB object will delegate the call to the bean,
* receive the result, and return it to us.
*
* We then print the result to the screen.
*/
System.out.println(hello.hello());
/*
* Done with EJB Object, so remove it.
* The container will destroy the EJB object.
*/
hello.remove();
}
}
-Djava.naming.provider.url=corbaloc::localhost:3700/NameService examples.HelloClient
Hello, World!
We need to identify the initial context factory class in the naming service provider we use and the url for
the root of the naming service.
To Remember…
If your bean calls another bean’s method and wants to pass its referecence as a parameter, then you must
pass a reference to your bean’s EJB object, rather than a reference to your bean ie passing this wont work
rather use the context object and call sessionCtx.getEJBObject() to get a reference to your EJB object and
pass that as a reference, where sessionCtx is the priavate variable where you stored the session context
object in the body of setSessionContext().
Also to allow for compile time checks for your bean class whether it implements all the business methods
or not, pull out the business methods into a separate interface which can be extends by your remote and
local interfaces and that your bean class will implement.
// Business interface
public interface HelloBusinessMethods {
public String hello() throws java.rmi.RemoteException;
}
// Bean implementation – now checked at compile time for compliance with the business methods interface
public class HelloBean implements SessionBean, HelloBusinessMethods {
public String hello() {
return “Hello, World!”;
}
<...define other required callbacks...>
}
WSDL
wscompile – tool to generate WSDL from Java interface.
Webservices are managed by container. JSR921 defines the programming model for webservices. It defines
a port component for the server-side view of a web service. It is a portable java implementation of a service
interface (port) and comprises java mapping of the service interface and the implementation bean. Writing
web service using EJB requires creating one or more port components as stateless session beans. After
exposing the bean as a web service only repackaging and redeploying is required, which now contains a
port component. The EJB container will know how to dispatch incoming SOAP messages to our bean
implementation and how to map the incoming XML data types to Java. The way to implement the service
endpoint in an existing bean is to define the business methods interface separately and have the remote
intrerface and the bean class implement this interface (as described earlier to provide compile time
checking of business methods being implemented in the bean class):
package examples;
/**
* This is the Hello remote interface.
*
*/
public interface HelloInterface extends java.rmi.Remote
{
/**
* The one method - hello - returns a greeting to the client.
*/
public String hello() throws java.rmi.RemoteException;
}
The extends java.rmi.Remote is added to the new definition of Hello interface. Remember that this
interface is extended by Hello remote interface (which also extends javax.ejb.EJBObject). This interface is
also called the Service Endpoint Interface (SEI).
Note: The bean class may not implement this interface explicitly but the business methods in the SEI must
anyhow be implemented by the bean class.
JAX/RPC defines a mapping between a set of supported Java types and WSDL/XML types. Java primitive
types are directly supported by JAX/RPC. Some non primitive types supported are: Wrappers (Boolean,
Integer, Character, etc), java.net.URI, java.util.Calendar, java.util.Date and java.lang.String.
Where,
Config.xml:
<?xml version="1.0" encoding="UTF-8"?>
<configuration xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/config">
<service
name="HelloWorldWS"
targetNamespace="urn:examples"
typeNamespace="urn:examples"
packageName="examples">
<interface name="examples.HelloInterface"/>
</service>
</configuration>
Config.xml defines the name of the service, the target namespace to use in the WSDL xml schema, the
name of the SEI.
<package-mapping>
<package-type>examples</package-type>
<namespaceURI>urn:examples</namespaceURI>
</package-mapping>
<service-interface-mapping>
<service-interface>examples.HelloWorldWS</service-interface>
<wsdl-service-name xmlns:serviceNS="urn:examples">serviceNS:HelloWorldWS</wsdl-service-
name>
<port-mapping>
<port-name>HelloInterfacePort</port-name>
<java-port-name>HelloInterfacePort</java-port-name>
</port-mapping>
</service-interface-mapping>
<service-endpoint-interface-mapping>
<service-endpoint-interface>examples.HelloInterface</service-endpoint-interface>
<wsdl-port-type xmlns:portTypeNS="urn:examples">
portTypeNS:HelloInterface
</wsdl-port-type>
<wsdl-binding xmlns:bindingNS="urn:examples">
bindingNS:HelloInterfaceBinding
</wsdl-binding>
<service-endpoint-method-mapping>
<java-method-name>hello</java-method-name>
<wsdl-operation>hello</wsdl-operation>
<wsdl-return-value-mapping>
<method-return-value>java.lang.String</method-return-value>
<wsdl-message xmlns:wsdlMsgNS="urn:examples">
wsdlMsgNS:HelloInterface_helloResponse
</wsdl-message>
<wsdl-message-part-name>result</wsdl-message-part-name>
</wsdl-return-value-mapping>
</service-endpoint-method-mapping>
</service-endpoint-interface-mapping>
</java-wsdl-mapping>
And package everything together in the ejb jar file as shown below:
Deploy this jar in the usual way and access it at url ??. (This part will be completed after I have read the
J2EE Webservices book).
Entity Beans
Entity beans are persistent objects.
public interface javax.ejb.EntityBean extends javax.ejb.EnterpriseBean {
public void setEntityContext(javax.ejb.EntityContext);
public void unsetEntityContext();
public void ejbRemove();
public void ejbActivate();
public void ejbPassivate();
public void ejbLoad();
public void ejbStore();
}
Entity beans can persist themselves in many ways, including Java serialization, O/R mapping, or even an
object database persistence. O/R mappings are the most frequently used mechanism in practice.
Entity beans are generally accessed by other beans that run-in process (typically with a session façade in
which case a session bean will access the entity bean). Clients (servlets/jsp/applets) invoke a method on the
session façade and the session bean in turn calls the entity bean. Since the session and entity beans will be
colocated in the same JVM process so the entity beans only expose local interface in production (and not
remote).
The container automatically synchronizes the in-memory entity bean instance with the database record they
represent by calling ejbLoad() and ejbStore() callbacks.
There can be multiple in-memory entity bean instances which represent the same underlying data. The data
corruption is prevented in such circumstances by synchronizing the beans with the underlying storage. The
frequency of such synchronizations is determined by the transaction isolation level associated with the
bean. If it is read-commited then two clients accessing the entity bean instances mapped to same data can
read the data only when the modification to it has been saved/committed to the storage. An uncommitted
data change will not be read.
The container may pool and reuse the entity bean instances to represent different instances of the same type
of data in the database (eg. Entity bean instance to represent different bank account records). Reassigning
the entity bean instance to represent a different data record requires having to let the bean know to release
any resource it held while mapping to the older data record. For this, entity bean class will need to
implement ejbActivate() and ejbPassivate(). The container invokes ejbStore() before passivating the
bean and invokes ejbLoad() after activating the bean.
Types
Bean Managed Persistent entity beans require you to write code to translate your in-memory fields into an
underlying data store. You need to provide implementations for ejbCreate(), ejbLoad(), ejbStore(),
ejbFindXXX() and ejbRemove() methods using JDBC API.
Container Managed Persistent entity beans requires you to provide the entity bean O/R mapping in the
deployment descriptor and container generates the data access code.
Bean return a primary key to the container so that the container can identify the bean uniquely, upon
ejbCreate() being called. On ejbRemove(), the database data associated with the bean is deleted and the
bean is made inaccessible to the client. Client can call remove() method of either EJBObject or Home
object.
In bean class:
public AccountPK ejbCreate(String accountID, String owner) throws...
In home interface:
public Account create(String accountID, String owner) throws ...
Home interface exposes finder methods in addition to create and remove methods.
If you directly create/modify/delete the data by directly touching the database, the in-memory entity beans
will be created (by just calling findByPrimaryKey() method on the home object for your bean), modified
(upon automatic synchronization) and deleted. So its not necessary for an entity bean to have an
ejbCreate() method as they can be created by directly touching the database.
Entity Context
An entity bean has a context object (which implements javax.ejb.EntityContext) via which it can query the
container for its own transaction state and security information.
getPrimaryKey() :
You call it whenever you want to figure out with which database data your instance is associated. Your
entity bean must perform a getPrimaryKey() callback to the entity context to figure out what data it is
dealing with. (Remember: entity beans can be pooled and reused to cater to different data records). In case
of BMP, you will need to use it for the following method implementations:
1. ejbLoad() : when ejbStore() is called the bean has the knowledge of what data it holds but when
the container calls its ejbLoad() it should first use getPrimaryKey() to know what data to be
fetched from the database.
2. ejbRemove() : when ejbCreate() is called the bean knows what data to create as it has been passed
to it in parameters to ejbCreate(). But while removing the data, you must call the getPrimaryKey()
to find out what data to remove.
Finder methods:
Are defined on local/remote home interfaces and implemented by your bean implementations to find one or
more existing entity bean in storage. You must define atleast one finder method –
ejbFindByPrimaryKey().
For eg. You might perform a query as “SELECT id FROM accounts WHERE balance > 10000” and return
the primary keys for the resultset data back to the container by creating one or more primary key object
instances. Container will then create EJB objects for the client to invoke on and possibly associate some
entity bean instances with those EJB objects.
Collection is collection of primary keys. Container then creates a collection of EJB Objects one for each
primary key in the collection, and returns those EJB Objects in its own collection to the client.
1. All finder methods must being with ejbFind.
2. Must have atleast one finder method – ejbFindByPrimaryKey().
public AccountPK ejbFindByPrimaryKey(AccountPK key) throws FinderException {
//…
}
Note: You define finder methods only in case of BMP. For CMP, these method implementations are
generated by container.
Entity bean instance returns a primary key to the container, whereas the home object returns an EJB Object
to the client.
We are not showing the code for the remote interface below but for testing we use the remote interface
itself. Though in practice, we will use a session façade to interface remotely with the client and use the
entity beans locally but to test the entity bean individually without writing a session façade we use the
remote interface.
Local interface
package examples.bmp;
import javax.ejb.*;
/**
* This is the local interface for AccountBean.
*
* Local clients interact with beans through this interface. The container will
* implement this interface; the implemented object is called the local object,
* which delegates invocations to the actual bean.
*/
public interface AccountLocal extends EJBLocalObject {
/**
* Deposits amt into account.
*/
public void deposit(double amt) throws AccountException;
/**
* Withdraws amt from bank account.
* @throw AccountException thrown if amt > available balance
*/
public void withdraw(double amt) throws AccountException;
/**
* Finds an Account by its primary Key (Account ID)
*/
public AccountLocal findByPrimaryKey(AccountPK key) throws FinderException;
/**
* Finds all Accounts under an owner’s name
*/
public Collection findByOwnerName(String name) throws FinderException;
/**
* This home business method is independent of any particular
* account instance. It returns the total of all the bank
* accounts in the bank.
*/
public double getTotalBankValue() throws AccountException;
}
hashCode() and equals() should be overridden to allow the container to be able to compare two primary key
instances by value when stored in a HashMap.
package examples.bmp;
import java.sql.*;
import javax.naming.*;
import javax.ejb.*;
import java.util.*;
/**
* Demonstration Bean-Managed Persistent Entity Bean. This Entity Bean
* represents a Bank Account.
*/
public class AccountBean implements EntityBean {
protected EntityContext ctx;
//
// Bean-managed state fields
//
private String accountID; // PK
private String ownerName;
private double balance;
public AccountBean() {
System.out.println(“New Bank Account Entity Bean Java Object created by EJB
Container.”);
}
//
// Business Logic Methods
//
/**
* Deposits amt into account.
*/
public void deposit(double amt) throws AccountException {
System.out.println(“deposit(“ + amt + “) called.”);
balance += amt;
}
/**
* Withdraws amt from bank account.
* @throw AccountException thrown if amt > available balance
*/
public void withdraw(double amt) throws AccountException {
System.out.println(“withdraw(“ + amt + “) called.”);
if (amt > balance) {
throw new AccountException(“Your balance is “ +
balance + “! You cannot withdraw “
+ amt + “!”);
}
balance -= amt;
}
// Getter/setter methods on Entity Bean fields
public double getBalance() {
System.out.println(“getBalance() called.”);
return balance;
}
public void setOwnerName(String name) {
System.out.println(“setOwnerName() called.”);
ownerName = name;
}
public String getOwnerName() {
System.out.println(“getOwnerName() called.”);
return ownerName;
}
public String getAccountID() {
System.out.println(“getAccountID() called.”);
return accountID;
}
public void setAccountID(String id) {
System.out.println(“setAccountID() called.”);
this.accountID = id;
}
/**
* This home business method is independent of any
* particular account instance. It returns the total
* of all the bank accounts in the bank.
*/
public double ejbHomeGetTotalBankValue() throws AccountException {
PreparedStatement pstmt = null;
Connection conn = null;
try {
System.out.println(“ejbHomeGetTotalBankValue()”);
/* Acquire DB connection */
conn = getConnection();
/* Get the total of all accounts */
pstmt = conn.prepareStatement(“select sum(balance) as total from accounts”);
ResultSet rs = pstmt.executeQuery();
/* Return the sum */
if (rs.next()) {
return rs.getDouble(“total”);
}
}
catch (Exception e) {
e.printStackTrace();
throw new AccountException(e);
}
finally {
/*
* Release DB Connection for other beans
*/
try { if (pstmt != null) pstmt.close(); }
catch (Exception e) { }
try { if (conn != null) conn.close(); }
catch (Exception e) { }
}
throw new AccountException(“Error!”);
}
/**
* Gets JDBC connection from the connection pool.
*
* @return The JDBC connection
*/
public Connection getConnection() throws Exception {
try {
Context ctx = new InitialContext();
javax.sql.DataSource ds =
(javax.sql.DataSource)ctx.lookup(“java:comp/env/jdbc/ejbPool”);
return ds.getConnection();
} catch (Exception e) {
System.err.println(“Couldn’t get datasource!”);
e.printStackTrace();
throw e;
}
}
//
// EJB-required methods
//
/**
* Called by Container. Implementation can acquire
* needed resources.
*/
public void ejbActivate() {
System.out.println(“ejbActivate() called.”);
}
/**
* Removes entity bean data from the database.
* Corresponds to when client calls home.remove().
*/
public void ejbRemove() throws RemoveException {
System.out.println(“ejbRemove() called.”);
/*
* Remember that an entity bean class can be used to
* represent different data instances. So how does
* this method know which instance in the database
* to delete?
*
* The answer is to query the container by calling
* the entity context object. By retrieving the
* primary key from the entity context, we know
* which data instance, keyed by the PK, that we
* should delete from the DB.
*/
AccountPK pk = (AccountPK) ctx.getPrimaryKey();
String id = pk.accountID;
PreparedStatement pstmt = null;
Connection conn = null;
try {
/*
* 1) Acquire a new JDBC Connection
*/
conn = getConnection();
/*
* 2) Remove account from the DB
*/
pstmt = conn.prepareStatement(“delete from accounts where id = ?”);
pstmt.setString(1, id);
/*
* 3) Throw a system-level exception if something
* bad happened.
*/
if (pstmt.executeUpdate() == 0) {
throw new RemoveException(
“Account “ + pk + “ failed to be removed from the database”);
}
}
catch (Exception ex) {
*/
try { if (pstmt != null) pstmt.close(); }
catch (Exception e) { }
try { if (conn != null) conn.close(); }
catch (Exception e) { }
}
}
/**
* Finds Accounts by name
*/
public Collection ejbFindByOwnerName(String name) throws FinderException {
PreparedStatement pstmt = null;
Connection conn = null;
Vector v = new Vector();
try {
System.out.println(“ejbFindByOwnerName(“ + name + “) called”);
/*
* Acquire DB connection
*/
conn = getConnection();
/*
* Find the primary keys in the DB
*/
pstmt = conn.prepareStatement( “select id from accounts where ownerName
= ?”);
pstmt.setString(1, name);
ResultSet rs = pstmt.executeQuery();
/*
* Insert every primary key found into a vector
*/
while (rs.next()) {
String id = rs.getString(“id”);
v.addElement(new AccountPK(id));
}
/*
* Return the vector of primary keys
*/
return v;
}
catch (Exception e) {
throw new FinderException(e.toString());
}
finally {
/*
* Release DB Connection for other beans
*/
try { if (pstmt != null) pstmt.close(); }
catch (Exception e) { }
try { if (conn != null) conn.close(); }
catch (Exception e) { }
}
}
}
Deployment Descriptor
<?xml version=”1.0” encoding=”UTF-8”?>
<ejb-jar xmlns=”http://java.sun.com/xml/ns/j2ee” version=”2.1”
xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation=”http://java.sun.com/xml/ns/j2ee/ejb-jar_2_1.xsd”>
<display-name>AccountJAR</display-name>
<enterprise-beans>
<entity>
<ejb-name>AccountEJB</ejb-name>
<home>examples.bmp.AccountHome</home>
<remote>examples.bmp.Account</remote>
<ejb-class>examples.bmp.AccountBean</ejb-class>
<persistence-type>Bean</persistence-type>
<prim-key-class>examples.bmp.AccountPK</prim-key-class>
<reentrant>false</reentrant>
<resource-ref>
<res-ref-name>jdbc/bmp-account</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
<res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>
</entity>
</enterprise-beans>
<assembly-descriptor>
<container-transaction>
<method>
<ejb-name>AccountEJB</ejb-name>
<method-intf>Remote</method-intf>
<method-name>*</method-name>
</method>
<method>
<ejb-name>AccountEJB</ejb-name>
<method-intf>Local</method-intf>
<method-name>*</method-name>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
</assembly-descriptor>
</ejb-jar>
Persistence-type Bean/Container
Prim-key-class Primary key class
Reentrant Whether our bean can call itself through another
bean. A given bean A is reentrant if it calls bean B
which calls back on bean A.
Resource-ref Sets up our jdbc driver at jndi location
Assembly-descriptor Associates transactions with out bean.
You will also need the AppServer specific deployement descriptor where you can configure the remote and
local home interface and the JDBC driver with JNDI locations.
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2004 Sun Microsystems, Inc. All rights reserved.
SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
-->
<!DOCTYPE sun-ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Application Server 8.0 EJB 2.1//EN"
"http://www.sun.com/software/appserver/dtds/sun-ejb-jar_2_1-0.dtd">
<sun-ejb-jar>
<enterprise-beans>
<name>BMPEntityEjb.jar</name>
<unique-id>0</unique-id>
<ejb>
<ejb-name>AccountEJB</ejb-name>
<jndi-name>AccountHome</jndi-name>
<resource-ref>
<res-ref-name>jdbc/bmp-account</res-ref-name>
<jndi-name>jdbc/bmp-account</jndi-name>
</resource-ref>
<bean-pool>
<steady-pool-size>0</steady-pool-size>
</bean-pool>
</ejb>
</enterprise-beans>
</sun-ejb-jar>
Create the accounts table in the database using the sql file provided. This step is database specific.
>mkdir assemble\ejbjar
Put the ejb-jar.xml and sun-ejb-jar.xml in the meta-inf folder in classes.
>jar cvf BMPEntityEJB.jar –C classes .
>move BMPEntityEJB.jar assemble\ejbjar
Client Code
package examples.bmp;
import javax.ejb.*;
import javax.naming.*;
import java.rmi.*;
import javax.rmi.*;
import java.util.*;
/**
* Sample client code that manipulates a Bank Account Entity Bean.
*/
public class AccountClient {
public static void main(String[] args) throws Exception {
Account account = null;
try {
/*
* Get a reference to the Account Home Object - the
* factory for Account EJB Objects
*/
Context ctx = new InitialContext(System.getProperties());
Object obj = ctx.lookup(“AccountHome”);
AccountHome home = (AccountHome)PortableRemoteObject.narrow(obj,
AccountHome.class);
System.err.println(“Total of all accounts in bank initially = “ +
home.getTotalBankValue());
/* Use the factory to create the Account EJB Object */
>mkdir assemble\client
10. Container calls newInstance() on the bean class to create an instance of bean. This calls entity
bean’s default constructor. Container associates the bean instance with an entity context by calling
setEntityContext() method of bean class. Now the bean is put in the instance pool for that entity
bean type.
11. In the pool, the bean instance can be used to find entity data in the database by servicing a finder
method on behalf of client. It can also perform operations not dependent on a particular data
instance by servicing ejbHome() method. Container can call the unsetEntityContext() when it
wants to destroy the bean instance from pool (and the bean will be ready for garbage collection).
12. When client wants to create some new database data then it calls the create() method on the home
interface. Container grabs an entity bean from pool and calls its ejbCreate() method where the
bean initializes itself to a specific data set passed in the params of the create() method by the client
and, in case of BMP, this method will also create the corresponding data record in the database.
Now the bean moves to ready state.
13. In the ready state, your bean is tied to a specific data and hence a specific EJB Object. Container
may occassionally need to synchronize your bean instance with the database by calling ejbLoad()
and ejbStore(). So ejbLoad() and ejbStore() will be responsible for loading and saving the data in
the database, in case of BMP.
14. If the client calls remove() on the home object or EJB object then container will call the
ejbRemove() method which in case of BMP is supposed to delete the database data associated
with this bean instance.
15. If the container needs to reuse a bean instance in the ready state to service another client, then it
will need to passivate the bean in which case it will call the ejbStore() method and then the
ejbPassivate() method, where the bean can release any resource it held. Then the bean is returned
back to pool for reuse.
16. Container may want to reassign the bean to an existing EJB object, then it will call the
ejbActivate() on the bean where the bean can reaquire the resouces it held earlier, and then the
container calls the ejbLoad() where the bean can initialize itself to the specific data (by first
learning about what primary key to use to retrieve that unique data record from the EJB object).
You don’t define any persistent fields and the associated get/set methods in your bean. They are kept in the
subclass (which the container generates).
You will need to declare the get/set methods as abstract methods in your entity bean class (thus
making it an abstract class). So if you need to provide a business method which works on more than one
persistent field then you can define such a method in your entity bean class itself and use the abstract
get/set methods in your implementation which will resolve to the subclasses implementation of those
get/set methods at runtime.
For example,
// CMP superclass – you write.
public abstract class CartBean implements EntityBean {
// no fields
}
// other get/set method implementations
}
Abstract persistence schema is defined in the ejb-jar.xml to let the container know what to generate for the
persistence logic. The container derieves the type of these fields from the get/set abstract methods.
EJB QL
EJB Query Language is an object-oriented SQL-like syntax for querying entity beans. You write EJB QL in
the deployment descriptor and the container should be able to generate the corresponding database logic in
SQL in a database specific way. For example, for the container to generate the implementation for the
finder method – findBigAccounts() we need to tell the container what query to perform on the database (in
a portable way using EJB QL in the DD) to generate the implementation for this method.
SELECT OBJECT(a)
FROM Account as a
WHERE a.balance > ?1
Note: Not all fields have to be managed by container. You might be pulling data from secondary sources or
you might have calculated fields. EJB container will notify your bean class during persistent operations
allowing you to manage these fields.
We can tell the container how to write the ejbSelectAllAccountBalances() method just like we do for finder
methods – using EJB QL in the DD.
SELECT OBJECT(a)
FROM Account AS a
WHERE a.balance IS NOT NULL
1. ejbSelect() methods are used internally within the bean and not exposed to clients.
2. Can retrieve data from other entity beans that you have relationship with.
3. Like finder methods, ejbSelect() methods can return primary key(s) or they can even return
container managed fields (a.balance above) which finder methods cannot (they must only return
primary key or collection of primary keys).
The remote, home, local and local home interfaces and primary key class remain same for both BMP and
CMP. What changes is the bean class code and the deployement descriptor. The client code does not change
if we change between BMP and CMP implementations.
Steps to follow for CMP entity bean coding: (differences from BMP highlighted in red).
17. setEntityContext() – store the entity context in a member varible. After this method executes,
bean does not hold a data yet but is available in pool.
18. ejbFindXXX() – Donot write this method in bean class, for CMP. Use EJB QL and the container
tools to tell the container what logic to execute when the client performs a finder method on the
home object.
19. ejbSelectXXX() {only in CMP}– Define this method as abstract and provide the EJB QL in the
deployment descriptor to set up the query. These are helper methods which perform queries
internally by your bean and are not accessible to clients of your bean.
20. ejbHomeXXX() {partially similar to BMP}– methods on an entity bean that are not specific to
any data record. Eg. Counting the total number of records in a table. They are special business
methods, as they are called from bean in pool before the bean is associated with any data. Clients
call home methods from local/remote home objects. For CMP, you provide this method in the bean
class and do the JDBC code yourself or have the container do it for you by using the
ejbSelectXXX() helper method mechanism described above for ejbHomeGetTotalBankValue().
21. ejbCreate() – you may not write any ejbCreate if you don’t want EJB clients to be able to create
new database data. These methods are responsible for creating new data record and initializing
your bean. For CMP, donot create database data here, rather just validate the client’s initialization
parameters and call your abstract set methods to initialize the generated bean subclass. Entity bean
instance moves to the ready state with ejbCreate().
22. ejbPostCreate() – must define one ejbPostCreate() for each ejbCreate() and both should take the
same parameters. Its called after container has associated an EJB object with your bean instance
(ie bean is in the ready state). You can get the primary key from the EJB object and use it here.
1. You can do any post initialization work here and/or
2. pass the reference to your bean’s EJB Object to other beans and/or
3. reset certain transaction related parameteres.
ejbActivate() – acquire any resource (such as socket conncetions) if needed.
ejbLoad() – Don’t read data here. Container will do that right before it calls your ejbLoad(). Here
you can perform any utilities you need to work with the read-in data (such as decompressing a text
field etc) using the get/set abstract methods.
ejbStore() – Don’t update the database here. Container will do that right after it calls you
ejbStore() method. So in this method you can prepare (eg compressing a text field) your container-
managed fields (those in the subclass) to be written in the database using the get/set abstract
methods.
ejbPassivate() – release any resource if needed.
ejbRemove() – Don’t delete the database data here. Container calls this method before actually
deleting the data from the database itself. Just perform any operation that must be done before the
data is destroyed.
unsetEntityContext() – release any resource aquired in setEntityContext() and get ready to be
garbage collected. Called when entity bean instance is to be destroyed by container when it wants
to reduce its pool size.
Changing the previous example (for BMP) to use CMP, we just need to re-write the bean class and the
deployment descriptor.
import javax.ejb.*;
/**
* Entity bean the demos CMP.
*/
public abstract class AccountBean implements EntityBean {
protected EntityContext ctx;
// no data fields
public AccountBean() {
System.out.println("New Bank Account Entity Bean Java Object created by EJB
Container.");
}
//-------------------
// Begin abstract get/set methods
//-------------------
public abstract double getBalance();
public abstract void setOwnerName(String name);
public abstract String getOwnerName();
public abstract String getAccountID();
public abstract void setAccountID(String id);
public abstract void setBalance(double balance);
//-------------------
// End abstract get/set methods
//-------------------
//-------------------
// Begin EJB required methods
//-------------------
/**
* Called by Container. Implementation can acquire
* needed resources.
*/
/**
* Called by Container. Releases held resources for
* passivation.
*/
public void ejbPassivate() {
System.out.println("ejbPassivate () called.");
}
/**
* Called by the container. Updates the in-memory entity
* bean object to reflect the current value stored in
* the database.
*/
public void ejbLoad() {
System.out.println("ejbLoad() called.");
}
/**
* Called from the Container. Updates the database
* to reflect the current values of this in-memory
* entity bean instance.
*/
public void ejbStore() {
System.out.println("ejbStore() called.");
}
/**
* Called by the container. Associates this bean
* instance with a particular context. We can query
* the bean properties which customize the bean here.
*/
public void setEntityContext(EntityContext ctx) {
System.out.println("setEntityContext called");
this.ctx = ctx;
}
/**
* Called by Container. Disassociates this bean
* instance with a particular context environment.
*/
public void unsetEntityContext() {
System.out.println("unsetEntityContext called");
this.ctx = null;
}
/**
* Called after ejbCreate(). Now, the Bean can retrieve
* its EJBObject from its context, and pass it as
* a 'this' argument.
*/
public void ejbPostCreate(String accountID, String ownerName) {
}
//
// Business Logic Methods
//
/**
* Deposits amt into account.
*/
public void deposit(double amt) throws AccountException
{
System.out.println("deposit(" + amt + ") called.");
setBalance(getBalanace() + amt);
}
/**
* Withdraws amt from bank account.
* @throw AccountException thrown if amt > available balance
*/
public void withdraw(double amt) throws AccountException
{
System.out.println("withdraw(" + amt + ") called.");
if (amt > getBalance()) {
throw new AccountException("Your balance is " + getBalance() +
"! You cannot withdraw " + amt + "!");
}
setBalance(getBalance() – amt);
}
/**
* This home business method is independent of any
* particular account instance. It returns the total
* of all the bank accounts in the bank.
*/
public double ejbHomeGetTotalBankValue() throws Exception
{
// get a collection of bank account balances
Collection c = this.ejbSelectAllAccountBalances();
// loop through the collection and return sum
Iterator it = c.iterator();
double sumTot = 0.0;
while(it.hasNext()) {
sumTot += ((Double) it.next ()).doubleValue();
}
return sumTot;
}
}
Deployment Descriptor
We need to inform the container about our entity bean, the container managed fields and the associated EJB
QL for the finder and select methods.
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar xmlns="http://java.sun.com/xml/ns/j2ee"
version="2.1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/ejb-
jar_2_1.xsd">
<display-name>Account</display-name>
<enterprise-beans>
<entity>
<ejb-name>AccountBean</ejb-name>
<home>examples.cmp.AccountHome</home>
<remote>examples.cmp.Account</remote>
<ejb-class>examples.cmp.AccountBean</ejb-class>
<persistence-type>Container</persistence-type>
<prim-key-class>examples.cmp.AccountPK</prim-key-class>
<reentrant>false</reentrant>
<cmp-version>2.x</cmp-version>
<abstract-schema-name>ACCOUNTS</abstract-schema-name>
<cmp-field>
<field-name>accountID</field-name>
<field-name>ownerName</field-name>
<field-name>balance</field-name>
</cmp-field>
<primarykey-field>accountID</primarykey-field>
<query>
<query-method>
<method-name>findByPrimaryKey</method-name>
<method-params>
<method-param>examples.cmp.AccountPK</method-param>
</method-params>
</query-method>
<ejb-ql>
SELECT OBJECT(a) FROM ACCOUNTS a WHERE a.accountID = ?1
</ejb-ql>
</query>
<query>
<query-method>
<method-name>findByOwnerName</method-name>
<method-params>
<method-param>java.lang.String</method-param>
</method-params>
</query-method>
<ejb-ql>
SELECT OBJECT(a) FROM ACCOUNTS a WHERE a.ownerName = ?1
</ejb-ql>
</query>
<query>
<query-method>
<method-name>selectAllAccountBalances</method-name>
<method-params/>
</query-method>
<ejb-ql>
<![CDATA[SELECT OBJECT(a) FROM ACCOUNTS a WHERE a.balance > 0]]
</ejb-ql>
</query>
</entity>
</enterprise-beans>
<assembly-descriptor>
<container-transaction>
<method>
<ejb-name>AccountBean</ejb-name>
<method-intf>Remote</method-intf>
<method-name>*</method-name>
</method>
<method>
<ejb-name>AccountBean</ejb-name>
<method-intf>Home</method-intf>
<method-name>*</method-name>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
</assembly-descriptor>
</ejb-jar>
[Note: ejbSelect does not work! Comment out in the bean code and the DD. Problem in EJB QL.]
All EJB QL that contain < or > must be enclosed in CDATA sections (or else the XML parser will confuse a
< or > as part of some xml tag and complain).
Client (code in the BMP section of this document) used to test BMP, can be used to test CMP entity bean
too without any modification.
Note: Its always better to use a primary key class even when you have a single field as primary key.
Wrapping that field in a primary key class aids in encapsulating the fields and write code in terms of the
primary key class. So in future if the primary key class uses some other field then code which depends on
the primary key class will still remain same.
Messaging Domains
Pub/sub domain is an implementation of distributed event processing model. Subscribers register their
interest in a particular event topic. Publishers (event sources) create messages (events) that are distributed
to all of the subscribers. Publishers donot know who all are interested in the events.
Point to point: you can have only a single consumer for each message. Multiple consumers can grab the
message off the queue but any given message is consumed exactly once. Multiple producers can send
messages to the queue.
createTopicSession takes two params: false means we don’t want to use transactions and
Session.AUTO_ACKNOWLEDGE means to auto acknowledge the receipt of a message. This is meant for
receiver of the message. Since we are sending messages to the topic so we don’t really care about this
param.
Message driven beans are message consumers. It was introduced in EJB 2.0. It can receive JMS messages
as well as other types of messages (using pluggable message providers feature for receiving non JMS
messages – this feature support was added in EJB 2.1 Spec). Message driven beans consume messages of a
given message type in accordance with the message listener interface it employs, that is JMS-based
message driven beans will implement javax.jms.MessageListener interface. As of EJB 2.1, Message
driven beans can employ different listener interfaces to consume different message types in addition to JMS
(like EIS specific messages). This is achieved with help from J2EE connector architecture 1.5. So you
can provide resource adapters to asynchronously deliver messages to message endpoints (MDBs)
residing in the app server independent of specific messaging type or semantics. So resource adapters
can act as message providers. Resource adapters are pluggable to any J2EE compliant app server. For
example, to receive EbXML messages in your enterprise application, we can write a EbXML resource
adapter which provides a specific messaging listener interface (say, com.xyz.EbXMLMessageListener)
which can be implemented by MDBs so as to receive EbXML messages. Thus, any enterprise application
can send any type of messages to a MDB via J2EE connector architecture based resource adapter.
A client cannot access a MDB through a component interface. They can only send messages to the MDB
which it will receive and process. So, MDBs don’t have home, local home, remote or local interface.
The resource adapter (via the container) calls the onMessage() (or similar method as defined by the specific
message listener interface that MDB implements) upon arrival of a message. The
javax.jms.MessageListener has onMessage() method which can accept a JMS message of types:
1. ByteMessage ,
2. ObjectMessage,
3. TextMessage,
4. StreamMessage or
5. MapMessage.
You can use instanceof operator to determine the exact type of a message at runtime.
Listener method(s):
1. May not have a return value
2. Might not send back an exception to clients but can generate a system level exception due to a
unchecked exception unhandled in the code and container will handle such exceptions.
3. Message driven beans are stateless. Thus multiple instances of MDB can process multiple messages
from the JMS destination or resource adaptor consurrently.
Bean class
public interface javax.jms.MessageListener {
public void onMessage(Message message);
}
package examples;
import javax.ejb.*;
import javax.jms.*;
/**
* Sample JMS Message-Driven Bean
*/
/**
* Associates this Bean instance with a particular context.
*/
public void setMessageDrivenContext(MessageDrivenContext ctx) {
this.ctx = ctx;
}
/**
* Initializes the bean
*/
public void ejbCreate() {
System.err.println(“ejbCreate()”);
}
/**
* Our one business method
*/
public void onMessage(Message msg) {
if (msg instanceOf TextMessage) {
TextMessage tm = (TextMessage) msg;
try {
String text = tm.getText();
System.err.println(“Received new message : “ + text);
}
catch(JMSException e) {
e.printStackTrace();
}
}
}
/**
* Destroys the bean
*/
public void ejbRemove() {
System.err.println(“ejbRemove()”);
}
}
Note: A MDB can register with EJB timer service for time based notifications by implementing
javax.ejb.TimedObject interface. Container will then invoke the bean’s ejbTimeout() method upon timer
expiration.
MDB class code is independent of destination type (queue or topic). The deployment descriptor determines
whether a topic or a queue is consumed.
import javax.naming.*;
import javax.jms.*;
import java.util.*;
publisher.publish(msg);
Note:
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.enterprise.naming.SerialInitContextFactory");
env.put(Context.PROVIDER_URL, "localhost:1099");
Context initialContext = new InitialContext(env);
Notice that the code does not use the property names such as java.naming. factory.initial, but instead
uses Context.INITIAL_CONTEXT_FACTORY and Context.PROVIDER_URL. This is because the
javax.naming.Context interface defines a set of constants for the names of the properties that you need to
set. Thus, you do not have to remember strings such as java.naming.factory.initial. This also makes your
code more flexible because it is independent of any changes that might be made to the property names in
future versions of JNDI. You will see more on the different properties and their names shortly.
Although it is possible to hard code the JNDI properties, it is the first two approaches that are the most
suitable for production environments. For both, all that you need to do is distribute a text file with the
application.
When you create an InitialContext, JNDI searches for any application resource files called
jndi.properties on the classpath. JNDI also looks in the Java runtime home directory (which is the jre
subdirectory in the Java JDK home directory) for a file called lib\jndi.properties. All the properties
that you define in these files are placed into the environment that belongs to the initial context.
For example, the j2ee.jar file in the lib directory of the J2EE RI contains these lines::
java.naming.factory.initial=com.sun.enterprise.naming.SerialInitContextFactory
java.naming.factory.url.pkgs=com.sun.enterprise.naming
These are a set of properties, which are simply name/value pairs. In practice, as long as the j2ee.jar file is
on the classpath, you should be all set.The first of these two properties, java.naming.factory.initial,
enables you to set the fully qualified class name of the Initial Context Factory for the JNDI Service
Provider. That is, you use this property to specify which JNDI Service Provider you want to use.
If you want to use the default naming service supplied with the J2EE RI (and the j2ee.jar file is not on
your classpath), you would use the following line in your jndi.properties file:
java.naming.factory.initial=com.sun.enterprise.naming.SerialInitContextFactory
Sun Microsystems provides several free reference implementations that are mentioned in the Table 14.1.
You can specify the values from the table for the Context.INITIAL_CONTEXT_FACTORY environment
property. Sun Microsystems maintains a list of service providers for JNDI on its Web site at
http://java.sun.com/products/jndi/serviceproviders.html.
You can find more information on these properties in the documentation for the javax.naming.Context
and Sun's JNDI Tutorial (http://java.sun.com/products/jndi/tutorial/index.html).
Deployment Descriptor
<?xml version='1.0' encoding='UTF-8'?>
<ejb-jar
xmlns="http://java.sun.com/xml/ns/j2ee"
version="2.1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/ejb
-jar_2_1.xsd"
>
<display-name>LogEjb</display-name>
<enterprise-beans>
<message-driven>
<ejb-name>LogBean</ejb-name>
<ejb-class>examples.LogBean</ejb-class>
<messaging-type>javax.jms.MessageListener</messaging-type>
<transaction-type>Bean</transaction-type>
<message-destination-type>javax.jms.Topic</message-destination-type>
<message-destination-link>PhysicalTopic</message-destination-link>
</message-driven>
</enterprise-beans>
<assembly-descriptor>
<message-destination>
<message-destination-name>PhysicalTopic</message-destination-name>
</message-destination>
</assembly-descriptor>
</ejb-jar>
Since the names of actual topic and queue deployed into JMS server are application server specific, the
mapping of a bean’s container to a specific JMS server destination needs to be done in the application
server specific deployment descriptor.
The following example illustrates the use of message destination linking in the deployment descriptor.
<enterprisebeans>
<session>
...
<ejbname>EmployeeService</ejbname>
<ejbclass>com.wombat.empl.EmployeeServiceBean</ejbclass>
...
<messagedestinationref>
<messagedestinationrefname>
jms/EmployeeReimbursements
</messagedestinationrefname>
<messagedestinationtype>javax.jms.Queue</messagedestinationtype>
<messagedestinationusage>Produces</messagedestinationusage>
<messagedestinationlink>ExpenseProcessingQueue</messagedestinationlink>
</messagedestinationref>
</session>
...
<messagedriven>
<ejbname>ExpenseProcessing</ejbname>
<ejbclass>com.wombat.empl.ExpenseProcessingBean</ejbclass>
<messagingtype>javax.jms.MessageListener</messagingtype>
...
<messagedestinationtype>javax.jms.Queue</messagedestination
type>
<messagedestinationlink>ExpenseProcessingQueue</messagedestination
link>
...
</messagedriven>
...
</enterprisebeans>
...
<assemblydescriptor>
...
<messagedestination>
<messagedestinationname>ExpenseProcessingQueue</messagedestination
name>
</messagedestination>
...
</assemblydescriptor>
The Application Assembler uses the messagedestinationlink element to indicate that the message destination reference
EmployeeReimbursement declared in the EmployeeService enterprise bean is linked to the ExpenseProcessing
message-driven bean by means of the commen destination ExpenseProcessingQueue.
You can use the deploy tool for the app server to create the JMS Resources : Connection resource as well as
the Destination Resource. You will be asked to provide a JNDI name when you create these resources. In
our case, they should be jms/TopicConnectionFactory (connection resource) and jms/Topic (destination
resource).
Deploy the EJB jar file for your bean. Run the client to publish the message on the Topic, and check the
server log file where the bean will have printed out the message received.
The container that subscribes to a topic consumes any given message only once. So if five instances of
MDB exist in the pool, only one of the instances will receive any particular message. Other instances can
be used to concurrently process other messages published on the same topic.
If same MDB is deployed to many containers in a cluster, then each deployment of the JMS MDB will
consume a message from the topic it subscribes to. So the same message will be processed by an MDB
instance residing in different containers in the cluster. If you want that each message is consumed only
once in a cluster, then deploy a Queue instead of a Topic. For a Queue type destination, the JMS server
will deliver any message on the queue to only one consumer (container). Each container registers as a
consumer to the Queue and the JMS server load-balances messages to the consumers based on consumer’s
availability. JMS MDBs that bind to Queues that are deployed in a cluster are ideal for scalable
processing of messages.
JMS server does not guarantee delivery of messages to a pool of JMS MDBs in any particular order. So
MDBs should be prepared to process messages that are not in sequence.
You can use Queues to partition business processing in a cluster as shown below:
Thus two different queues can be used to partition the business processing in a cluster. The production
queue will experience more load from real users so the cluster size for production stack systems can be
scaled to accommodate more traffic.
A poison message is a message that is continuously re-transmitted by a JMS destination to the consumer
because the consumer continuously fails to acknowledge the consumption of the message. This can happen
if in case of container managed transaction, the MDB calls messageDrivenContext.setRollbackOnly()/or
throws some system level exception, every time forcing the transaction to be rolled back and the container
will not send an acknowledgement in this case so the destination will resend the message to the consumer.
To avoid this case, its best not to throw a system level exception (like EJBException) and MDB’s should
not throw application level exception (its discouraged practice as application level exceptions are meant for
clients and MDBs don’t have clients), so just log the error and return (thus ignoring the message which
caused the error and continue to process next message). Remember, application level exceptions when
thrown will not lead to a transaction roll back.
If Bean managed transaction is used for MDB instead of container managed transaction, then the container
does not have any transaction to rollback and thus the acknowledgement of message is always carried out.
So use Bean Managed Transaction to avoid the possibility of poison messages.
EJB spec does not define any mechansim that specifies how MDBs can propagate a response back to client
that originally generated the message. Heres a strategy to employ when you want the client to receive the
responses from MDBs (in a somewhat secure way guaranteeing fault tolerance, message filtering etc). For
more alternatives refer page 249-253 of Mastering EJB 3rd Edn book.
Application properties can be set by setTextMessage() method on the client end using JMS API. Security
can be improved by introducing a session EJB (stateless/stateful) as the topic that response messages are
delived to can be made available only internally by simply not exposing it to direct client access. A fixed
OutgoingResponseTopic is created at the JMS server where the responses are posted by the MDBs. Further
application properties can be added to the outgoing message by the client (for eg. If the client sends out
more than one request then it might also want to filter on the RequestID parameter with which it can
associate an incoming response message with a request.)
// Create a bean
Catalog c = home.create(...);
java:comp/env/ejb context is the recommended context in which to put your bean names that are
referrenced from other beans.
EJB References
An EJB reference is a nickname for the JNDI location that you want to look up a bean. Your code then
looks up a home reference via this nickname and the deployer then binds that nickname (ejb reference) to
the JNDI location of its choice. For example,
...
<enterprise-beans>
<!--
Here, we define our Catalog bean. Notice we use the
“Catalog” ejb-name. We will use this below.
-->
<session>
<ejb-name>Catalog</ejb-name>
<home>examples.CatalogHome</home>
...
</session>
<session>
<ejb-name>Pricer</ejb-name>
<home>examples.PricerHome</home>
...
<ejb-ref>
<description>
This EJB reference says that the Pricing Engine
session bean (Pricer) uses the Catalog Engine
session bean (Catalog)
</description>
<!--
The nickname that Pricer uses to look
up Catalog. We declare it so the deployer
knows to bind the Catalog home in
java:comp/env/ejb/CatalogHome. This may not
correspond to the actual location to which the
deployer binds the object via the container
tools. The deployer may set up some kind of
symbolic link to have the nickname point to the
real JNDI location.
-->
<ejb-ref-name>ejb/CatalogHome</ejb-ref-name>
Similar to the message-destination-link we saw earlier where a JMS destination was being shared between
two beans linking by the message-desitination-name. Similarly, when one bean has to lookup another
bean’s home interface then it can link to the bean by the ejb-link tag using bean name (optionally) and has
to specify the nickname with which it will be looking up the bean. The deployer will use the information in
the DD under Pricer bean to bind the Catalog bean with the nickname (or use a JNDI symbolic link) in the
JNDI tree. The Catalog bean could also reside in some other Ejb jar file than Pricer bean using its own DD.
A bean can also access another bean using its Local interface in the same fashion. Only change the
1. <ejb-ref> to <ejb-local-ref>,
2. <home> to <local-home> and
3. <remote> to <local>.
// Create a bean
CatalogLocal c = home.create(...);
Resource Factories
A resource factory is the driver that gives you connections (eg JDBC/JMS driver or JCA resource
adapter). EJB mandates that you need to use JNDI to lookup a resource factory.
// Obtain the initial JNDI context
Context initCtx = new InitialContext();
<res-sharing-scope>Sharable</res-sharing-scope>
</resource-ref>
</session>
</enterprise-beans>
...
Environment Properties
These are application specific properties that your beans read in at run time. Container reads in this DD and
makes the environment properties available for your bean at runtime. These are put in java:comp/env
context or in some subcontext of this context.
...
<enterprise-beans>
<session>
<ejb-name>Pricer</ejb-name>
<home>examples.PricerHome</home>
...
<!--
This element contains a single environment property.
The property is only accessible from the Pricer.
-->
<env-entry>
<description>
The algorithm for this pricing engine.
</description>
<!--
The JNDI location that Pricer uses to look up
the environment property. We declare it so the
container knows to bind the property in
java:comp/env/PricerProperties/algorithm.
-->
<env-entry-name>Pricer/algorithm</env-entry-name>
<!-- The type for this environment property -->
<env-entry-type>java.lang.String</env-entry-type>
<!-- The environment property value -->
<env-entry-value>NoTaxes</env-entry-value>
</env-entry>
</session>
</enterprise-beans>
...
So if you have some items in your shopping cart (viz implemented as a stateful session bean) and you
happen to loose connection, you’d want your shopping cart state maintained when you reconnect. Here’s
how you use it:
// First, get the EJB object handle from the EJB object.
javax.ejb.Handle myHandle = myEJBObject.getHandle();
// time passes...
Home Handles
Persistent references to Home Objects. They may be useful because you can acquire a reference to a home
object, persist it, and then use it again later without knowledge of the home object’s JNDI location.
// First, get the EJB home handle from the home object.
javax.ejb.HomeHandle homeHandle = myHomeObject.getHomeHandle();
// time passes...
If distributed transactions are a must for your application then Spring + POJO will not work. But Spring
framework does support EJB based development of application. They provide a lightweight framework for
writing business tier (lightweight because they provide declarative local transaction for POJO without
relying on the container.)
StrutsEJB project (http://strutsejb.dev.java.net) provides base classes and patterns (mainly business
delegate, service locator, DTO, and Session Façade) to build Struts web application that uses EJB in the
business tier.
Struts also provides tag libraries for generating device markups (WML, HDML, and XHTML) for small
device support.
Model Driven Development (MDD) : model typically represents entity in the problem domain. MDD tools
like Compuware OptimalJ or AndoMDA MDD can generate core J2EE design patterns (with discretion
from the architect) from model diagrams. IBM RDD (Rational Rapid Developer) donot support any
specific standard for MDD but have their own paradigm. MDD suits better for new applications. Refer
http://middlewareresearch.com/endeavors/031126MDDCOMP/endeavor.jsp.
Extreme Programming (XP) is a software engineering discipline whose core practices revolve around an
underlying assumption: Change in requirements and hence software design will inevitably occur during the
course of software development. Original thinker of XP – Kent Beck.
Core principles are:
1. Iterative development: development is given a certain target to meet per iteration (checkpoints or
milestones). Features for the iteration are defined.
2. EJB subphase: development phase for designing, developing, building and deploying EJBs on
development systems.
3. Testing subphase: build unit test clients.
4. User subphase: present work done to actual users who test the application using the acceptance
test plan. Avoid adding new features in the middle of the iteration.
5. Continuous Integration: keeping various application components in sync with each other so that
system is fully integrated most times. Builds should be made atleast once a day. Idea is not to defer
integration until last moment.
6. Refactoring: process of continuously improving the design of existing code without affecting
code behavior. Involves restructuring of code so as to remove redundant code, reduce coupling in
code, introduce better naming conventions in the code, or organize the code more cohesively. For
more, refer http://industriallogic.com/xp/refactoring/catalog.html.
7. Test-driven development: prime focus for XP.
XP is lightweight compared to more formal design processes like Rational Unified Process (RUP) or now
obsoleted waterfall model.
Aspect-Oriented Programming (AOP): aspects are the core of AOP. Aspects are reusable services that
are quintessentially crosscutting to your application (eg, services that provide user authentication,
authorization, logging of access to system, and persistence of application data). Crosscutting concerns
because developer cannot write robust applications without taking care of them. Hence AOP is a
programming platform that facilitates the development of aspects to mitigate concerns so that aspects can
be reused by all living objects within a given environment.
EJB containers implement these crosscutting concerns and provide reusable aspects so that all the beans
deployed within the container can offload these concerns on the container aspects. So EJB container is an
Aspect Oriented Implementation. EJB programming model does not allow you to develop new aspects to
take care of concerns that are not supported by EJB container. So J2EE is not a AOP platform even though
EJB uses AOP techniques. In order to use AOP in EJB, you can use frameworks like Spring AOP, AspectJ
or tools provided by App Server vendors. JBoss and Websphere provide AOP support. So until EJB spec
allows for AOP as standard, you loose portability using AOP with custom vendor tools.
POJOs are used to get around contraints imposed by EJB standard – like accessing filesystem or
read/write static fields (you cannot use static fields in your EJBs). We might also end up reusing these
POJOs outside the EJB container, for example, in a Web application or a Swing application. In this
scenario, it might be better to write reusable aspects to address concerns relevant to your POJOs. For
example, consider a POJO that reads and writes to a file. Here you might need to log the
timestamp of the last updating of the file. In which case, you would create an aspect to address the logging
concern. This way you can use the Logging aspect no matter whether your POJO is being used within an
EJB container or within a Web container or in a vanilla Java Swing application.
OOP does not work when behavior needs to be reused horizontally, owing to the behavior’s crosscutting
nature. Now why would you need to reuse behavior horizontally? Because you do not want your business
object, for example a ShoppingCart, to inherit the behavior pertaining to transactions, since these behaviors
are unrelated; you do not want an apple to inherit grape-like qualities; rather you want to mix apples and
grapes to prepare a margarita drink. This is where AOP comes into picture. AOP and OOP are not
competing but complementary technologies. Consider, for example, an EJB server where crosscutting
aspects are provided to your object-oriented beans.
EJB programming APIs support only static invocation (where information about the interface and
methods to invoke on its object are known at the compile time).
Reflection facilitates dynamic (runtime) invocation of methods already implemented in class. Dynamic
proxy approach allows dynamically implementing a proxy class that implements a list of interfaces
specified by class at runtime. Proxy is generated dynamically and method invocation on the proxy object
(for eg. java.lang.reflect.Proxy) is dispatched to a single method named invoke() which takes the method
name and invokes it via java.lang.reflect.Method object and takes the method arguments as Object[]. Thus
dynamic proxy object uses reflective mechanism and provides a generic object that is capable of
intercepting methods from clients on behalf of any server object implementing the interfaces this dynamic
proxy implements. You can use it to dynamically invoke methods on EJB when client does not have a priori
knowledge of interfaces implemented by EJB.
To automate deployment of your EJB applications on multiple application servers, you can use XDoclet
framework. XDoclet is a powerful, attribute oriented code generation engine. Using XDoclet, a developer
can generate practically anything – XML descriptors (DDs – both standard and server specific ones), source
code and so on – by inserting attributes (metadata) within Javadoc for their source. Uses are: Generate code
for Value classes, primary key classes, struts action form based on Entity EJB, and home and remote
interfaces. Also integration of XDoclet with ANT through Ejbdoclet tasks makes it even more powerful
framework for deployments.
Transactions
We must guarantee data integrity even when many users concurrently update the data. A transaction is a
series of operations that appear to execute as one large, atomic operation. Databases also offer concurrency
control with different levels of isolation. But concurrency control for an enterprise application is not only
required at datasource level but at the application level where you might have several steps in a business
logic being performed and you require them to be one atomic operation – and of these steps, accessing a
datasource might just be one part but within your EJB the transaction service will guarantee that if any
unchecked or remote exception is thrown then container will rollback the transaction in which case revert
your bean state to the original state and in case you used CMP for your datasource access then it will even
not commit the data change you made to the datasource. Thus effectively the state of bean(s) will be
changed only on success of all steps constituting the business method (or a series of them if multiple beans
are involved and the same transaction is in effect for those beans – it is possible to start a new transaction
for a bean’s business method though in which case if the new transaction rolls back and the business
method throws an unchecked exception to the calling bean’s business method then the original transaction
will also be rolled back). Since beans are single threaded so concurrency control is provided by the
container by default by executing other bean instance of same type (from the instance pool) in case of
concurrent requests. The bean code does not have to code for concurrency control.
Transaction Jargons
4. Transaction Object: A transactional object (or transactional component) is an application
component, such as a banking component, that is involved in a transaction.
5. Transaction Manager: A transaction manager is responsible for managing the
transactional operations of the transactional components. It manages the entire overhead of a
transaction, running behind the scenes to coordinate things
6. Resource: A resource is a persistent storage from which you read or write. A resource could be
a database, a message queue, or other storage.
7. Resource Manager: A resource manager manages a resource. An example of a resource
manager is a driver for a relational database, object database, message queue, or other store.
Resource managers are responsible for managing all state that is permanent. The most popular
interface for resource managers is the X/Open XA resource manager interface. Most database
drivers support this interface. Because X/Open XA is the de facto standard for resource managers,
a deployment with heterogeneous XA resource managers from different vendors can interoperate.
Transactional Models
Flat Transactions: is a series of operations that are performed atomically as a
single unit of work. Flat transactions can be chained too but if one of the
subtransaction rolls back then it causes the outer transactions to rollback too. This is
the behavior supported by EJB transaction service.
Nested Transactions: Anested transaction enables you to embed atomic units of work within
other units of work. The unit of work that is nested within another unit of work can roll back without
forcing the entire transaction to roll back. Therefore the larger unit can attempt to retry the embedded unit
of work. If the embedded unit can be made to succeed, the larger unit can succeed. If the embedded unit of
work cannot be made to work, it will ultimately force the entire unit to fail.
What’s special about nested transactions is that subtransactions can independently roll back without
affecting higher transactions in the tree. That’s a very powerful idea, and it solves our famous trip-
planning problem: If each individual booking is a nested transaction, we can roll back any one booking
without canceling all our other reservations. But in the end, if the nested transaction cannot be committed,
the entire transaction will fail.
EJB spec does not mandate support for nested transactions though so all
transactions are flat for EJBs.
The bean’s business method executes in the transaction context controlled by the
container (which delegates to the bean for executing the business method). So if
there is any system level execption in the bean then the transaction is rolled back by
the container. If a session bean’s transaction is already been initiated and in its
business method the bean invokes a method on an entity bean then the entity bean
can either leverage the same transactional context (Supports/Required transactional
attribute) and execute within it or it can tell the container to start a new transactional
context (RequiresNew) for its own use.
You can even have a client-initiated transaction in which case a servlet/JSP tag
library, application/applet or CORBA client or another enterprise bean which is client
to your bean can being and end transactions in that client.
A corollary of this discussion is that entity beans do not load and store their data on every method call;
rather, they load and store their data on every transaction. If your entity beans are not performing well,
it could be because a transaction is happening on each method call, and thus a database read/write is
happening on every get/set method. The solution is to make sure your transactions begin
earlier and end later, perhaps encompassing many entity bean method calls. By
properly controlling the duration of your transactions with transaction attributes, you can control when
database reads and writes happen with entity beans.
Transaction Attributes
A transaction attribute is a setting that you give to a bean to control how your bean is enlisted in container-
managed transactions. The transactional attribute is a required part of each bean’s deployment descriptor.
The container knows how transactions should be handled with a bean by reading that bean’s transaction
attribute from its deployment descriptor. Note that you can specify transaction attributes for entire beans or
for individual bean methods. If both are specified, then method-level attributes take precedence.
<assembly-descriptor>
<!--
This demonstrates setting a transaction attribute
on every method on the bean class.
-->
<container-transaction>
<method>
<ejb-name>Employee</ejb-name>
<method-name>*</method-name>
</method>
<!--
Transaction attribute. Can be “NotSupported”,
“Supports”, “Required”, “RequiresNew”,
“Mandatory”, or “Never”.
-->
<trans-attribute>Required</trans-attribute>
</container-transaction>
<!--
You can also set transaction attributes on individual methods.
-->
<container-transaction>
<method>
<ejb-name>Employee</ejb-name>
<method-name>setName</method-name>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
<!--
You can even set different transaction attributes on
methods with the same name that take different parameters.
-->
<container-transaction>
<method>
<ejb-name>Employee</ejb-name>
<method-name>setName</method-name>
<method-param>String</method-param>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
</assembly-descriptor>
You must specify transaction attributes on all business methods for your beans. Furthermore, with entity
beans you must specify transaction attributes that cover home interface methods, because the home
interface creation methods insert database data and thus need to be transactional.
transaction management of RA components. RA can support either a local transaction, which is managed
internally by the resource manager, or it can support a distributed transaction, whose coordination does
involve external transaction managers. If RA that supports local transactions, the client component, such as
an EJB, will have to acquire the common client interface API object, such as
javax.resource.cci.LocalTransaction or an equivalent from the resource adapter to demarcate the
transactions. If RA supports distributed transactions, the container will automatically enlist the client in the
transaction context, if the client wants to work within a distributed transaction. J2EE Connector
Architecture 1.5 supports the inflow of transactions fromEnterprise Information System (EIS) to the J2EE
environment. This is a powerful addition because it enables the J2EE applications to participate in
transactions initiated by backend EIS. For example, you can make your stateless session bean participate in
a transaction that was initiated in the Tuxedo environment, given that the underlying RA supports this
contract.
Required
You should use the Required mode if you want your bean to always run in a transaction. If a transaction is
already running, your bean joins in on that transaction. If no transaction is running, the EJB
container starts one for you.
The customer wants to use the credit card component to charge a user’s credit card when a user purchases a
product from a Web site. The customer then wants to submit an order to manufacture that product, which is
handled by a separate component. Thus, the customer has two separate components running but both of
them run under the same transaction. If the credit card cannot be charged, the customer doesn’t want the
order to be submitted. If the order cannot be submitted, the customer doesn’t want the credit card charged.
Therefore the customer produces his or her own workflow bean, which first calls our credit card–charging
bean and then calls the bean to generate a manufacturing order. The workflow bean is deployed with
Required, so a transaction automatically starts up. Because your credit card bean is also deployed with
Required, you join that transaction, rather than start your own transaction. If the order submission
component is also deployed with Required, it joins the transaction as well. The container commits or aborts
the transaction when the workflow bean is done.
RequiresNew
You should use the RequiresNew attribute if you always want a new transaction to begin when your
bean is called. If a transaction is already under way when your bean is called, that transaction is suspended
during the bean invocation. The container then launches a new transaction and delegates the call to the
bean. The bean performs its operations and eventually completes. The container then commits or aborts the
transaction and finally resumes the old transaction. RequiresNew is useful if your bean needs the ACID
properties of transactions but wants to run as a single unit of work without allowing other external
logic to also run in the transaction.
Supports
When a bean is called with Supports, it runs in a transaction only if the client had one running
already. If the client does not have a transaction, the bean runs with no transaction at all. Because Supports
will sometimes not run within a transaction, you should be careful when using this attribute. Mission-
critical operations should be encapsulated with a stricter transaction attribute (like Required).
Mandatory
Mandatory mandates that a transaction must be already running when your bean method is called. If a
transaction isn’t running, the javax.ejb.TransactionRequiredException exception is thrown back to the
caller (or javax.ejb.Tranasction- RequiredLocalException if the client is local). Mandatory is useful if your
component is designed to run within a larger system, such as a workflow system, where your bean is only
part of a larger suite of operations, and you want to mandate that the larger operations start a transaction
before calling your bean.
NotSupported
If you set your bean to use NotSupported, then your bean cannot be involved in a transaction at all.
For example, assume we have two enterprise beans, Aand B. Let’s assume bean A begins a transaction and
then calls bean B. If bean B is using the NotSupported attribute, the transaction that A started is suspended.
None of B’s operations are transactional, such as reads/writes to databases. When B completes, A’s
transaction is resumed.
You should use NotSupported if you are certain that your bean operations do not need the ACID properties.
This should be used only if your beans are performing non–mission-critical operations, where you are not
worried about isolating your bean’s operations from other concurrent operations. An example here is an
enterprise bean that performs rough reporting. If you have an e-commerce Web site, you might write a
bean that routinely reports a rough average number of e-commerce purchases per hour by scanning
a database. Because this is a low-priority operation and you don’t need exact figures, NotSupported is
an ideal, low-overhead mode to use.
Never
The Never transaction attribute means that your bean cannot be involved in a transaction. Furthermore, if
the client calls your bean in a transaction, the container throws an exception back to the client
(java.rmi.RemoteException if remote, javax.ejb.EJBException if local).
This transaction attribute is useful when you want to make sure all clients that call your bean do not use
transactions. This can help reduce errors in client code, because a client will not be able to call your bean
erroneously in a transaction and expect your bean to participate in the ACID properties with other
transaction participants.
For example, let’s say you want to perform a transfer between two bank accounts. To achieve this, you
might have a bank teller session bean that calls into two bank account entity beans. If you deploy all three
of these beans with the Required transaction attribute, they will all be involved in a single transaction.
Entity beans and stateful session beans with SessionSynchronization must use transactions. The
reason is that both of these types of beans are inherently transactional in nature. Entity beans perform
database updates, and stateful session beans with SessionSynchronization (which we describe later in this
chapter) are also transactional. Therefore you normally can’t use the following attributes: Never,
NotSupported, Supports.
There is no client, and therefore transaction attributes that deal with the notion of a client’s
transaction make no sense for message-driven beans—namely Never, Supports, RequiresNew, and
Mandatory.
javax.transaction.UserTransaction
public interface javax.transaction.UserTransaction {
public void begin(); -- begin a new transaction
public void commit(); -- runs two-phase commit protocol on an existing transaction associated
with current thread. Each resource manager makes its update durable.
public int getStatus(); -- retrieves status of transaction associated with current thread.
public void rollback(); -- rollbacks the transaction associated with current thread.
public void setRollbackOnly(); -- force current transaction to rollback.
public void setTransactionTimeout(int); -- max. time that a transaction can run before its rolled
back.
}
JTA also defines a number of constants that indicate the current status of a
transaction.
public interface javax.transaction.Status {
public static final int STATUS_ACTIVE;
public static final int STATUS_NO_TRANSACTION;
public static final int STATUS_MARKED_ROLLBACK; -- in case some party called setRollbackOnly().
public static final int STATUS_PREPARING; -- Phase one of two phase commit protocol.
public static final int STATUS_PREPARED; -- Phase one completed.
public static final int STATUS_COMMITTING; -- Phase two happening.
public static final int STATUS_COMMITTED; -- Phase two completed.
public static final int STATUS_ROLLING_BACK;
public static final int STATUS_ROLLEDBACK;
public static final int STATUS_UNKNOWN;
A bean using the above method, must be deployed with transaction type of Bean. When using
programmatic transaction always try to complete your transactions in the same method in which you began
them.
setRollbackOnly
If your bean is participating in a transaction started by some other bean then you can doom a transaction (Ie
force a transaction to abort) by calling setRollbackOnly() on your EJB context object.
If your transaction participant is a POJO (and not an EJB component) then you will have to lookup the
UserTransaction object in the JNDI and call its setRollbackOnly(), as shown below:
try {
/*
* 1: Set environment up. You must set the JNDI Initial
* Context factory, the Provider URL, and any login
* names or passwords necessary to access JNDI. See
* your application server product’s documentation for
* details on their particular JNDI settings.
*/
java.util.Properties env = ...
/*
* 2: Get the JNDI initial context
*/
Context ctx = new InitialContext(env);
/*
* 3: Look up the JTA UserTransaction interface
* via JNDI. The container is required to
* make the JTA available at the location
* java:comp/UserTransaction.
*/
userTran = (javax.transaction.UserTransaction)
ctx.lookup(“java:comp/UserTransaction”);
/*
* 4: Execute the transaction
*/
userTran.begin();
// perform business operations
userTran.commit();
}
catch (Exception e) {
// deal with any exceptions, including ones
// indicating an abort.
}
In case there are say 10 beans in a chain executing the same transaction, and bean 2 decides to doom the
transaction then other beans should be able to detect that the transaction has been doomed and avoid
performing work when a doomed transaction exists:
1. Container managed transactional beans can detect doomed transactions by calling
getRollbackOnly() on their EJB Context object. If it returns true, then transaction is doomed.
2. Other participants, such as bean managed transactional beans, can get hold of UserTransaction
object from the JNDI and call its getStatus() method.
Transaction Isolation
Dirty Read
Adirty read occurs when your application reads data from a database that has not been committed to
permanent storage yet. Dirty reads can occur if you use the weakest isolation level, called READ
UNCOMMITTED. With this isolation level, if your transaction is executing concurrently with another
transaction, and the other transaction writes some data to the database without committing, your transaction
will read that data in.
The advantage of this isolation level is performance. The underlying transaction system doesn’t have to
acquire any locks on shared data in this mode.Your code will read committed data only when running in
READ COMMITTED mode. When you execute with this isolation level, you will not read data that has
been written but is uncommitted. This isolation level thus solves the dirty read problem. One great use
for this mode is for programs that read data from a database to report values of the data. Because reporting
tools aren’t in general missioncritical, taking a snapshot of committed data in a database makes sense.
READ COMMITTED is the default isolation level for most databases, such as Oracle or Microsoft SQL
Server.
Unrepeatable Read
Unrepeatable reads occur when a component reads some data from a database, but upon rereading the data,
the data has been changed. This can arise when another concurrently executing transaction modifies the
data being read.
REPEATABLE READ guarantees yet another property on top of READ COMMITTED: Whenever you
read committed data from a database, you will be able to reread the same data again at a later time, and the
data will have the same values as the first time. Hence, your database reads are repeatable. Use
REPEATABLE READ when you need to update one or more data elements in a resource, such as one or
more records in a relational database. You want to read each of the rows that you’re modifying and then be
able to update each row, knowing that none of the rows are being modified by other concurrent
transactions. If you choose to reread any of the rows at any time later in the transaction, you’d be
guaranteed that the rows still have the same data that they did at the beginning of the transaction.
Phantom Read
A phantom is a new set of data that magically appears in a database between two read operations. For
example, if your transaction reads a relational record, and a concurrent transaction commits a new record to
the database, a new phantom record appears that wasn’t there before. You can easily avoid phantoms (as
well as the other problems described earlier) by using the strictest isolation level: SERIALIZABLE.
SERIALIZABLE guarantees that transactions execute serially with respect to each other, and it enforces
the isolation ACID property to its fullest. This means that each transaction truly appears to be independent
of the others. Use SERIALIZABLE for mission-critical systems that absolutely must have perfect
transactional isolation. You are guaranteed that no data will be read that has been uncommitted. You’ll be
able to reread the same data again and again. And mysterious committed data will not show up in your
database while you’re operating due to concurrent transactions.
Note:
If your bean is managing transactions, you specify isolation levels with your resource manager API
(such as JDBC). For example, you could call java.sql.Connection.SetTransactionIsolation().
If your container is managing transactions, there is no way to specify isolation levels in the deployment
descriptor. You need to either use resource manager APIs (such as JDBC) or rely on your container’s tools
or database’s tools to specify isolation.
Unfortunately, there is no way to specify isolation for container-managed transactional beans in a portable
way—you are reliant on container and database tools. This means if you have written an application, you
cannot ship that application with built-in isolation. The best strategy is to develop EJBs that are as tolerant
of isolation differences as possible. This is the typical technique used by many optimistic concurrency
libraries that have been layered over JDBC and ODBC.
In the distributed two-phase commit, there is one master transaction manager called the distributed
transaction coordinator. The transaction coordinator runs the show and coordinates operations among the
other transaction managers across the network.
A distributed two-phase commit transaction complicates matters, because the transaction managers must all
agree on a standard mechanism of communicating. The communication mechanism used is called the
transactional communications protocol. The most important piece of information sent over the
transactional communications protocol is the transaction context. Atransaction context is an object that
holds information about the system’s current transactional state. It is passed around among parties involved
in transactions. By querying the transaction context, you can gain insight into whether you’re in a
transaction, what stage of a transaction you are at, and other useful data. For any component to be involved
in a transaction, the current thread in which the component is executing must have a transaction context
associated with it.
For a stateless session bean, aborting a business process is a simple task—simply throw an exception back
to the client. But for a stateful session bean, things are a bit trickier. Stateful session beans represent
business processes that span multiple method calls and hence have in-memory conversational state.
Tossing away that conversation and throwing an exception to the client could entail a significant amount of
lost work. Fortunately, a well-designed stateful session bean can salvage its conversations in the case of
failed transactions. The key is to design your beans to be aware of changes to conversational state and to be
smart enough to undo any of those changes if a transactional abort occurs.
Your application server can aid you in determining when a transaction failed, enabling you to take
application-specific steps. If your session bean needs to be alerted to transaction status (like failed
transactions), your enterprise bean class can implement an optional interface called
javax.ejb.SessionSynchronization.
The key method that is most important for rolling back conversations is afterCompletion(). The container
calls your afterCompletion() method when a transaction completes either in a commit or an abort. You can
figure out whether a commit or an abort happened by the Boolean parameter that gets passed to you in
afterCompletion(): True indicates a successful commit, false indicates an abort. If an abort happened, you
should roll back your conversational state to preserve your session bean’s conversation.
Note: You can use SessionSynchronization only with Stateful Session Beans using container managed
transactions.
Security
[Covered in my JBoss notes.]
With CMP, you declare the way you would like your relationships to work in your deployment descriptor.
The container then generates all the relationship code you need when the container subclasses your entity
bean.
<ejb-jar>
<enterprise-beans>
... define enterprise beans ...
</enterprise-beans>
<relationships>
... define EJB relationships ...
</relationships>
</ejb-jar>
Cardinality
The following code shows how to implement a one-to-one relationship using BMP:
public class OrderBean implements EntityBean {
private String orderPK;
private String orderName;
private Shipment shipment; // EJB local object stub
public Shipment getShipment() { return shipment; }
public void setShipment(Shipment s) { this.shipment = s;}
...
public void ejbLoad() {
// 1: SQL SELECT Order. This also retrieves the
// shipment foreign key.
//
// 2: JNDI lookup of ShipmentHome
//
// 3: Call ShipmentHome.findByPrimaryKey(), passing
// in the shipment foreign key
}
public void ejbStore() {
// 1: Call shipment.getPrimaryKey() to retrieve
// the Shipment foreign key
//
// 2: SQL UPDATE Order. This also stores the
// Shipment foreign key.
}
}
Our ejbLoad() method loads the database data of the order, and part of that data is a foreign key to the
shipment. We need to transform that foreign key into a stub to a shipment bean. Therefore we need to
perform a JNDI lookup of the shipment home and then call a finder method, passing in the foreign key.
This gives us a stub, and we can then call business methods on the shipment.
Our ejbStore() method stores the database data for the order, and part of that data is a foreign key to the
shipment. We need to transform the shipment stub into a foreign key. Therefore, we need to call
getPrimaryKey() on the shipment stub. This gives us our foreign key, and we can then perform the SQL.
The following code shows how to implement that same one-to-one relationship using CMP:
public abstract class OrderBean implements EntityBean {
// no fields
public abstract Shipment getShipment();
public abstract void setShipment(Shipment s);
...
public void ejbLoad() { } // Empty
public void ejbStore() { } // Empty
}
We do need to specify the relationship in the deployment descriptor, and we do so as follows:
<ejb-jar xmlns=”http://java.sun.com/xml/ns/j2ee” version=”2.1”
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
xsi:schemaLocation=”http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/ejb-jar_2_1.xsd”>
<enterprise-beans>
...
</enterprise-beans>
<relationships>
<!-- This declares a relationship -->
<ejb-relation>
<!-- The nickname we’re giving this relationship -->
<ejb-relation-name>Order-Shipment</ejb-relation-name>
<!--
This declares the 1st half of the relationship
(the Order side)
-->
<ejb-relationship-role>
<!-- The nickname we’re giving this half of the relationship -->
<ejb-relationship-role-name>
order-spawns-shipment
</ejb-relationship-role-name>
<!-- The Cardinality of this half of the relationship -->
<multiplicity>One</multiplicity>
<!--
The name of the bean corresponding to this
half of the relationship
-->
<relationship-role-source>
<ejb-name>Order</ejb-name>
</relationship-role-source>
<!--
Recall that a CMP entity bean has an abstract get/set
method for the relationship. We need to tell the
container which get/set method corresponds to this
relationship, so that the container can generate the
appropriate scaffolding code when sub-classing our bean.
That is the purpose of this element, which is called the
container-managed relationship (CMR) field. The value
of the CMR field should be the name of your get/set
method, but without the get/set, and with a slight
change in capitalization. getShipment() becomes shipment.
-->
<cmr-field><cmr-field-name>shipment</cmr-field-name></cmr-
field>
</ejb-relationship-role>
<!--
This declares the 2nd half of the relationship
(the Shipment side)
-->
<ejb-relationship-role>
<ejb-relationship-role-name>
shipment-fulfills-order
</ejb-relationship-role-name>
<multiplicity>One</multiplicity>
<relationship-role-source>
<ejb-name>Shipment</ejb-name>
</relationship-role-source>
</ejb-relationship-role>
</ejb-relation>
</relationships>
</ejb-jar>
The following code shows how to implement a one-to-many relationship using BMP:
public class CompanyBean implements EntityBean {
private String companyPK;
private String companyName;
private Vector employees; // EJB object stubs
The following code shows how to implement a one-to-many relationship using CMP:
public abstract class CompanyBean implements EntityBean {
// no fields
public abstract Collection getEmployees();
public abstract void setEmployees(Collection employees);
...
public void ejbLoad() { } // Empty
public void ejbStore() { } // Empty
}
The following code shows how to implement a many-to-many relationship as two one-to-many
relationships using BMP:
public class StudentBean implements EntityBean {
private String studentPK;
private String studentName;
...
public void ejbLoad() { // SQL SELECT Student }
public void ejbStore() { // SQL UPDATE Student }
}
public class CourseBean implements EntityBean {
private String coursePK;
private String courseName;
...
public void ejbLoad() { // SQL SELECT Course }
public void ejbStore() { // SQL UPDATE Course }
}
public class EnrollmentBean implements EntityBean {
private String enrollmentPK;
private Student student; // EJB local object stub
private Course course; // EJB local object stub
public Course getCourse() { return course; }
public void setCourse(Course c) { this.course = c;}
public Student getStudent() { return student; }
public void setStudent(Student s) { this.student = s; }
...
public void ejbLoad() {
// 1: SQL SELECT Enrollment. This loads both the
// Enrollment plus the foreign keys to Student
// and Course.
//
// 2: JNDI lookup of StudentHome, CourseHome
//
// 3: Call findByPrimaryKey() on both the Student
// and Course homes, passing the foreign keys
}
public void ejbStore() {
// 1: Call getPrimaryKey() on Student,Course. This
// gives us our foreign keys.
//
// 2: SQL UPDATE Enrollment
}
}
A brand-new entity bean, called enrollment, models the relationship between student and course. The
enrollment bean keeps a stub for a course and a stub for a student and has get/set methods for clients to
access those stubs. At the point in which object/relational mapping occurs, we transform those stubs to and
from their foreign key database representation.
The following code shows how to implement a fake many-to-many relationship using CMP:
public abstract class StudentBean implements EntityBean {
// no fields
...
public void ejbLoad() { } // Empty
public void ejbStore() { } // Empty
}
</relationship-role-source>
<cmr-field><cmr-field-name>course</cmr-field-name></cmr-field>
</ejb-relationship-role>
<ejb-relationship-role>
<ejb-relationship-role-name>
Course-Has-Enrollments
</ejb-relationship-role-name>
<multiplicity>One</multiplicity>
<relationship-role-source>
<ejb-name>Course</ejb-name>
</relationship-role-source>
</ejb-relationship-role>
</ejb-relation>
</relationships>
</ejb-jar>
We model our fake many-to-many relationship as two many-to-one relationships (one for each bean in the
relationship).
Directionality
The directionality of a relationship specifies the direction in which you can navigate a relationship. It can
be Bi-directional or Uni-directional.
In a bidirectional relationship, each bean in the relationship has a field pointing to the other bean, along
with a get/set method.
Birdirectional BMP:
public class OrderBean implements EntityBean {
private String orderPK;
private String orderName;
// EJB local object stub, must be stored/loaded
private Shipment shipment;
public Shipment getShipment() { return shipment; }
public void setShipment(Shipment s) { this.shipment = s; }
...
}
public class ShipmentBean implements EntityBean {
private String shipmentPK;
private String shipmentName;
// EJB local object stub, must be stored/loaded
private Order order;
public Order getOrder() { return order; }
public void setOrder(Order o) { this.order = o; }
...
}
Unidirectional BMP:
public class OrderBean implements EntityBean {
private String orderPK;
private String orderName;
// EJB local object stub, must be stored/loaded
private Shipment shipment;
public Shipment getShipment() { return shipment; }
public void setShipment(Shipment s) { this.shipment = s; }
...
}
public class ShipmentBean implements EntityBean {
private String shipmentPK;
private String shipmentName;
// No Order stub, no Order get/set method
...
}
Birdirectional CMP:
public abstract class OrderBean implements EntityBean {
// no fields
public abstract Shipment getShipment();
public abstract void setShipment(Shipment s);
...
public void ejbLoad() { } // Empty
public void ejbStore() { } // Empty
}
A composition relationship is an is-assembled-of relationship. For example, orders are assembled of line
items. Deleting an order deletes all line items. Line items shouldn’t be around if their parent order is gone.
An aggregation relationship does not cause a cascading delete, whereas a composition relationship
does. With BMP, you implement a cascading delete manually in your ejbRemove() method.
public class OrderBean implements EntityBean {
private String orderPK;
private String orderName;
private Shipment shipment; // EJB local object stub
Clustering
Sample Application
[fri]
http://java.sun.com/blueprints/code/projectconventions.html for how to arrange your project build
environment.
http://java.sun.com/blueprints/code/namingconventions.html for the naming conventions to follow for J2EE
applications.