Lesson 11 - Information Technology Gate

Download Report

Transcript Lesson 11 - Information Technology Gate

Session Beans




Entity beans provide an object-oriented model that makes it
easier for developers to create, modify, and delete data from the
database.
They allow developers to be more productive by encouraging
reuse, thus reducing development costs. For example, once a
bean has been defined to represent a concept like a ship, that
bean can be reused throughout a business system without
redefining, recoding, or retesting the business logic and data
access.
Session beans fill the gaps left by entity beans. They are useful
for describing interactions between other beans (taskflow) and
for implementing particular tasks.
Unlike entity beans, session beans do not represent data in the
database, but they can access data. This means that we can use
session beans to read, update, and insert data in a business
1
process.
Session Beans






When do you use an entity bean and when do you use a
session bean?
As a rule of thumb, an entity bean should provide a safe and
consistent interface to a set of shared data that defines a
concept.
This data may be updated frequently. Session beans access
data that spans concepts, is not shared, and is usually readonly. Session beans contain business logic and entity beans
model persistent data.
In addition to accessing data directly, session beans can
represent taskflow.
Taskflow refers to all the steps required to accomplish a
particular task, such as booking passage on a ship or renting a
video.
Session beans frequently manage the interactions among entity
beans, describing how they work together to accomplish a
specific task.
2
Session Beans




The relationship between session beans and entity beans is
like the relationship between a script for a play and the
actors that perform the play.
Actors are pointless without a script; they may represent
something, but they can't tell a story.
Session beans are divided into two basic types: stateless
and stateful.
A stateless session bean is a collection of related services,
each represented by a method; the bean maintains no state
from one method invocation to the next. When you invoke a
method on a stateless session bean, it executes the method
and returns the result without knowing or caring what other
requests have gone before or might follow.
3
Session Beans




Think of a stateless session bean as a set of procedures or
batch programs that execute a request based on some
parameters and return a result.
A stateful session bean is an extension of the client application.
It performs tasks on behalf of a client and maintains state
related to that client. This state is called conversational state
because it represents a continuing conversation between the
stateful session bean and the client.
Methods invoked on a stateful session bean can write and read
data to and from this conversational state, which is shared
among all methods in the bean.
Stateful session beans tend to be specific to one scenario.
They represent logic that might have been captured in the
client application of a two-tier system.
4
Session Beans




Stateless session beans have longer lives because they do
not retain any conversational state and are not dedicated to
one client.
As soon as a stateless session bean has finished a method
invocation, it can be reassigned to service a new client.
Whether they are stateful or stateless, session beans are
not persistent, like entity beans are.
In other words, session beans don't represent persistent
data and are not saved to the database.
5
The Stateless Session Bean




A stateless session bean is very efficient and relatively
easy to develop. A session bean can be swapped freely
between EJB objects because it isn't dedicated to one
client and doesn't maintain any conversational state.
As soon as it is finished servicing a method invocation it
can be swapped to another EJB object.
Because it does not maintain conversational state, a
stateless session bean does not require passivation or
activation, further reducing the overhead of swapping.
In short, stateless session beans are lightweight and
fast.
6
The Stateless Session Bean



Stateless session beans can be used for report
generation, batch processing, or some stateless
services such as validating credit cards.
Another good application might be a StockQuote EJB
that returns a stock's current price.
Any activity that can be accomplished in one method
call is a good candidate for the high-performance
stateless session bean.
7
The ProcessPayment EJB




The next section develops a complete definition of the
TravelAgent EJB, including the logic of the bookPassage( )
method.
At this point, however, we are primarily interested in the
ProcessPayment EJB, which is a stateless bean the
TravelAgent EJB uses to charge the customer for the price
of the cruise.
Because many different systems charge customers for
services, we've encapsulated the logic for charging
customers in its own bean.
Payments are recorded in a special database table called
PAYMENT.
8
The ProcessPayment EJB





The PAYMENT data is batch-processed for accounting
purposes and is not normally used outside of accounting.
In other words, the data is only inserted by Titan's system; it
is not read, updated, or deleted.
Because the process of making a charge can be completed
in one method, and because the data is not updated
frequently and is not shared, we will use a stateless session
bean for processing payments.
Several different forms of payment can be used: credit card,
check, and cash.
We will model these payment forms in our stateless
ProcessPayment EJB.
9
The database table: PAYMENT


The ProcessPayment EJB accesses an existing table in
Titan's system, called the PAYMENT table.
In your database, create a table called PAYMENT, with
this definition:
CREATE TABLE PAYMENT
(
customer_id
INTEGER,
amount
DECIMAL(8,2),
type
CHAR(10),
check_bar_code CHAR(50),
check_number
INTEGER,
credit_number
CHAR(20),
credit_exp_date DATE
)
10
The business interface: ProcessPayment





A stateless session bean has one or more business
interfaces. The business interface for ProcessPayment
obviously needs a byCredit( ) method because the
TravelAgent EJB uses it.
We can also identify two other methods that we'll need:
byCash( ) for customers paying cash and byCheck( ) for
customers paying with a personal check.
A business interface can be a remote or local interface, but
not both. Remote business interfaces are able to receive
method invocations from networked clients.
Local interfaces are available only within the same JVM as
the session bean.
Invoking on a local interface does not copy the parameters
or return value. Because of this, local interfaces are said to
follow what is termed call-by-reference semantics.
11
The business interface: ProcessPayment


Since the TravelAgent EJB will be in the same deployment,
the ProcessPayment EJB should provide a local interface so
that invocations on ProcessPayment are efficient. The
ProcessPayment EJB also has remote clients, so we'll provide
a remote business interface as well.
To make our design a little bit cleaner, we will have these
interfaces extend a base interface called
com.titan.processpayment.ProcessPayment :
package com.titan.processpayment;
import com.titan.domain.*;
public interface ProcessPayment {
public boolean byCheck(Customer customer, CheckDO check, double amount)
throws PaymentException;
public boolean byCash(Customer customer, double amount)
throws PaymentException;
public boolean byCredit(Customer customer, CreditCardDO card,
double amount) throws PaymentException; }
12
The business interface: ProcessPayment


The EJB specification allows you to define a common base
class for our remote and local interfaces if they share the
same methods.
Next, let's specify both the remote and local interfaces:
package com.titan.processpayment;
import javax.ejb.Remote;
@Remote
public interface ProcessPaymentRemote extends ProcessPayment {
}
package com.titan.processpayment;
import javax.ejb.Local;
@Local
public interface ProcessPaymentLocal extends ProcessPayment{
}
13
The business interface: ProcessPayment


The ProcessPaymentRemote and
ProcessPaymentLocal interfaces extend the base
ProcessPayment interface so that they do not have
to duplicate the method definitions.
ProcessPaymentRemote is identified as a remote
interface by the @javax.ejb.Remote annotation and
ProcessPaymentLocal by the @javax.ejb.Local
annotation.
14
Entities as parameters





Each method of the ProcessPayment EJB's business interface
takes a Customer entity bean as a parameter. Because entity
beans are plain Java objects, they can be serialized across the
network as plain Java objects as long as they implement
java.io.Serializable or Externalizable.
This is important because the ProcessPayment EJB accesses
the internal state of the Customer entity.
If every call to the Customer's get methods went over a remote
interface (as is required in the EJB 2.1 specification), the
ProcessPayment EJB would be very inefficient, because
network calls are expensive.
This is yet another example of the simplicity of the EJB 3.0
specification.
In EJB 2.1, because entities were first-class components, one
had to write parallel value object classes if one wanted to pass
around the state of an entity instead of accessing it through the
old remote interface model.
15
Domain objects: the CreditCardDO and
CheckDO classes

The ProcessPayment EJB's business interface uses two
classes that are particularly interesting, CreditCardDO and
CheckDO:
/* CreditCardDO.java */
package com.titan.processpayment;
import java.util.Date;
public class CreditCardDO implements java.io.Serializable {
final static public String MASTER_CARD = "MASTER_CARD";
final static public String VISA = "VISA";
final static public String AMERICAN_EXPRESS =
"AMERICAN_EXPRESS";
final static public String DISCOVER = "DISCOVER";
final static public String DINERS_CARD = "DINERS_CLUB_CARD";
16
Domain objects: the CreditCardDO and
CheckDO classes
public String number;
public Date expiration;
public String type;
public CreditCardDO (String nmbr, Date exp, String typ) {
number = nmbr;
expiration = exp;
type = typ;
}
}
/* CheckDO.java
*/
package com.titan.processpayment;
public class CheckDO implements java.io.Serializable {
public String checkBarCode;
public int checkNumber;
public CheckDO(String barCode, int number) {
checkBarCode = barCode;
checkNumber = number;
}
}
17
Domain objects: the CreditCardDO and
CheckDO classes



CreditCardDO and CheckDO are domain objects.
They are simply serializable Java classes, not
enterprise beans; they provide a convenient
mechanism for transporting related data.
CreditCardDO, for example, collects all the credit
card data together in one class, making it easier to
pass the information across the network as well as
making our interfaces a little cleaner.
18
An application exception: PaymentException





It is important to understand what exceptions you should use,
and when you should use them.
Exceptions such as javax.naming.NamingException and
java.sql.SQLException are thrown by other Java subsystems
and have nothing to do with the business process an EJB is
supposed to be modeling.
Checked exceptions (non-Runtime Exceptions), by default, do
not cause a transaction rollback.
Instead of throwing these types of exceptions directly, you
should catch them in a try/catch block and throw a more
appropriate exception.
A common practice is to wrap these checked exceptions in a
javax.ejb.EJBException , as these types of subsystem errors
are unrecoverable.
19
An application exception: PaymentException



EJBException is unchecked (it extends
java.lang.RuntimeException ), so you won't get a compile
error if you don't catch it.
A PaymentException describes a specific business problem
that is possibly recoverable. This makes it an application
exception.
The EJB container treats any exception that does not extend
RuntimeException as an application exception. Here is the
definition of PaymentException:
package com.titan.processpayment;
public class PaymentException extends java.lang.Exception {
public PaymentException( ) {
super( );
}
public PaymentException(String msg) {
super(msg);
}
}
20
The bean class: ProcessPaymentBean


The ProcessPayment EJB models a specific business
process, so it is an excellent candidate for a stateless
session bean.
This bean really represents a set of independent operations
- another indication that it is a good candidate for a stateless
session bean. Here is the definition of the
ProcessPaymentBean class:
package com.titan.processpayment;
import com.titan.domain.*;
import java.sql.*;
import javax.ejb.*;
import javax.annotation.Resource;
import javax.sql.DataSource;
import javax.ejb.EJBException;
21
The bean class: ProcessPaymentBean
@Stateless
public class ProcessPaymentBean implements ProcessPaymentRemote,
ProcessPaymentLocal {
final public static String CASH = "CASH";
final public static String CREDIT = "CREDIT";
final public static String CHECK = "CHECK";
@Resource(mappedName="titanDB") DataSource dataSource;
@Resource(name="min") int minCheckNumber;
public boolean byCash(Customer customer, double amount)
throws PaymentException {
return process(customer.getId( ), amount, CASH, null, -1, null, null);
}
public boolean byCheck(Customer customer, CheckDO check, double amount)
throws PaymentException {
if (check.checkNumber > minCheckNumber) {
return process(customer.getId( ), amount, CHECK,
check.checkBarCode, check.checkNumber, null, null);
}
else {
throw new PaymentException("Check number is too low.
Must be at least "+minCheckNumber);
}
}
22
The bean class: ProcessPaymentBean
public boolean byCredit(Customer customer, CreditCardDO card,
double amount) throws PaymentException {
if (card.expiration.before(new java.util.Date( ))) {
throw new PaymentException("Expiration date has passed"); }
else {
return process(customer.getId( ), amount, CREDIT, null,
-1, card.number, new java.sql.Date(card.expiration.getTime( )));
} }
private boolean process(int customerID, double amount, String type,
String checkBarCode, int checkNumber, String creditNumber,
java.sql.Date creditExpDate) throws PaymentException {
Connection con = null;
PreparedStatement ps = null;
try {
con = dataSource.getConnection( );
ps = con.prepareStatement
("INSERT INTO payment (customer_id, amount, type,"+
"check_bar_code,check_number,credit_number,"+
"credit_exp_date) VALUES (?,?,?,?,?,?,?)");
23
The bean class: ProcessPaymentBean
ps.setInt(1,customerID);
ps.setDouble(2,amount);
ps.setString(3,type);
ps.setString(4,checkBarCode);
ps.setInt(5,checkNumber);
ps.setString(6,creditNumber);
ps.setDate(7,creditExpDate);
int retVal = ps.executeUpdate( );
if (retVal!=1) {
throw new EJBException("Payment insert failed"); }
return true;
} catch(SQLException sql) { throw new EJBException(sql); }
finally {
try {
if (ps != null) ps.close( );
if (con!= null) con.close( );
} catch(SQLException se) {
se.printStackTrace( );
}
}
}
}
24
The bean class: ProcessPaymentBean

The bean class is annotated with the @javax.ejb.Stateless
annotation to identify that it is a stateless session bean:
Package javax.ejb;
@Target(TYPE) @Retention(RUNTIME)
public @interface Stateless {
String name( ) default "";
}


The three payment methods use the private helper method
process( ), which does the work of adding the payment to
the database.
This strategy reduces the possibility of programmer error
and makes the bean easier to maintain. The process( )
method does not use entity beans but simply inserts the
payment information directly into the PAYMENT table using
JDBC. The JDBC connection is obtained from the
datasource field of the bean class.
25
Accessing environment properties (injection)





The datasource and minCheckNumber fields are examples
of session bean fields that are initialized by the EJB's
environment.
Each EJB container has its own internal registry where it
stores configuration values and references to external
resources and services.
This registry is called the Enterprise Naming Context
(ENC).
If you look at the declaration of these member variables,
you will see that they are annotated with
@javax.annotation.Resource.
This tells the EJB container that when an instance of the
bean class is instantiated, those fields must be initialized
with values in the container's ENC.
26
Accessing environment properties (injection)

For minCheckNumber, the @Resource annotation identifies a
named value within the ENC that may be used to initialize the
field externally. This named value can be configured using the
EJB's XML deployment descriptor:
<ejb-jar
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation = " http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd"
version="3.0">
<enterprise-beans>
<session>
<ejb-name>ProcessPaymentBean</ejb-name>
<env-entry>
<env-entry-name>min
</env-entry-name>
<env-entry-type>java.lang.Integer</env-entry-type>
<env-entry-value>250</env-entry-value>
</env-entry>
</session>
</enterprise-beans>
</ejb-jar>

The XML populates the EJB's ENC under the name min with a
value of 250.
27
The XML Deployment Descriptor

Here is a deployment descriptor that provides a complete
annotation-alternative definition of the ProcessPayment EJB:
<?xml version="1.0"?>
<ejb-jar
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=
"http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd" version="3.0">
<enterprise-beans>
<session>
<ejb-name>ProcessPaymentBean</ejb-name>
<remote>com.titan.processpayment.ProcessPaymentRemote</remote>
<local>com.titan.processpayment.ProcessPaymentLocal</local>
<ejb-class>com.titan.processpayment.ProcessPaymentBean</ejb-class>
<session-type>Stateless</session-type>
<resource-ref>
<res-ref-name>theDatasource</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
<mapped-name>titandb</mapped-name>
<injection-target>
<injection-target-class>
com.titan.processpayment.ProcessPaymentBean
</injection-target-class>
<injection-target-name>dataSource</injection-target-name>
</injection-target>
</resource-ref>
28
The XML Deployment Descriptor
<env-entry>
<env-entry-name>min</env-entry-name>
<env-entry-type>java.lang.Integer</env-entry-type>
<env-entry-value>250</env-entry-value>
<injection-target>
<injection-target-class>
com.titan.processpayment.ProcessPaymentBean
</injection-target-class>
<injection-target-name>minCheckNumber </injection-target-name>
</injection-target>
</env-entry>
</session>
</enterprise-beans>
</ejb-jar>

The XML deployment descriptor schema also supports partial
XML definitions. For example, if you just wanted to configure
the minCheckNumber field in XML, you wouldn't have to
declare every piece of metadata about the EJB:
29
The XML Deployment Descriptor
<ejb-jar>
<session>
<ejb-name>ProcessPaymentBean</ejb-name>
<env-entry>
<env-entry-name>min</env-entry-name>
<env-entry-type>java.lang.Integer</env-entry-type>
<env-entry-value>250</env-entry-value>
</env-entry>
</session>
</ejb-jar>
30
SessionContext



The javax.ejb.SessionContext interface provides a view into
the EJB container's environment.
The SessionContext object can be used as the bean
instance's interface to the EJB container to obtain information
about the context of the method invocation call and to provide
quick access to various EJB services.
A session bean can obtain a reference to its SessionContext
by using the @Resource annotation:
@Stateless
public class ProcessPaymentBean implements ProcessPaymentLocal {
@Resource SessionContext ctx;
...
}
31
SessionContext


SessionContext allows you to obtain information such as the
current user that is invoking on the EJB or to look up entries
within the EJB's ENC.
Let's look at the javax.ejb.SessionContext interface:
public interface javax.ejb.SessionContext extends javax.ejb.EJBContext {
EJBLocalObject getEJBLocalObject( ) throws IllegalStateException
EJBObject getEJBObject( ) throws IllegalStateException;
MessageContext getMessageContext( ) throws IllegalStateException;
<T> getBusinessObject(Class<T> businessInterface)
throws IllegalStateException;
Class getInvokedBusinessInterface( );
}


The getEJBObject( ) and getEJBLocalObject( ) methods are
obsolete and will throw an exception if invoked on.
They are objects that are specific to the EJB 2.1 style of
defining EJBs.
32
SessionContext


The SessionContext.getBusinessObject( ) method returns a
reference to the current EJB that can be invoked on by other
clients. This reference is the EJB equivalent to Java's this
pointer.
The getBusinessObject( ) method allows the bean instance to
get its own EJB object reference, which it can then pass to other
beans. Here is an example:
@Stateless
public class A_Bean implements A_BeanRemote {
@Resource private SessionContext context;
public void someMethod( ) {
B_BeanRemote b = ... // Get a remote reference to B_Bean.
A_BeanRemote mySelf = getBusinessObject(A_BeanRemote.class);
b.aMethod( mySelf );
}
...
}

It is illegal for a bean instance to pass a this reference to
another bean; instead, it passes its remote or local EJB object
reference, which the bean instance gets from its
33
SessionContext.
EJBContext


SessionContext extends the javax.ejb.EJBContext class.
EJBContext defines several methods that provide useful
information to a bean at runtime.
Here is the definition of the EJBContext interface:
package javax.ejb;
public interface EJBContext {
public Object lookup(String name);
// EJB 2.1 only: TimerService
public TimerService getTimerService( )
throws java.lang.IllegalStateException;
// security methods
public java.security.Principal getCallerPrincipal( );
public boolean isCallerInRole(java.lang.String roleName);
34
EJBContext
// transaction methods
public javax.transaction.UserTransaction getUserTransaction( )
throws java.lang.IllegalStateException;
public boolean getRollbackOnly( )
throws java.lang.IllegalStateException;
public void setRollbackOnly( )
throws java.lang.IllegalStateException;
// deprecated and obsolete methods
public java.security.Identity getCallerIdentity( );
public boolean isCallerInRole(java.security.Identity role);
public java.util.Properties getEnvironment( );
public EJBHome getEJBHome( )
java.lang.IllegalStateException;
public EJBLocalHome getEJBLocalHome( )
java.lang.IllegalStateException;
}
35
The Life Cycle of a Stateless Session Bean

The life cycle of a stateless session bean is very simple. It
has only two states: Does Not Exist and Method-Ready Pool.
Stateless session bean life cycle
36
The Does Not Exist State


When a bean is in the Does Not Exist state, it is
not an instance in the memory of the system.
In other words, it has not been instantiated yet.
37
The Method-Ready Pool



Stateless bean instances enter the MethodReady Pool as the container needs them.
When the EJB server is first started, it may
create a number of stateless bean instances and
enter them into the Method-Ready Pool. (The
actual behavior of the server depends on the
implementation.)
When the number of stateless instances
servicing client requests is insufficient, more can
be created and added to the pool.
38
Transitioning to the Method-Ready Pool





When an instance transitions from the Does Not Exist state to
the Method-Ready Pool, three operations are performed on it.
First, the bean instance is instantiated by invoking the
Class.newInstance( ) method on the stateless bean class.
Second, the container injects any resources that the bean's
metadata has requested via an injection annotation or XML
deployment descriptor.
Finally, the EJB container will post a post-construction event.
The bean class can register for this event by annotating a
method with @javax.annotation.PostConstruct .
This annotated method is called by the container after the bean
is instantiated.
The callback method can be of any name, but it must return
void , have no parameters, and throw no checked exceptions.
The bean class may define only one @PostConstruct method
(but it is not required to do so).
39
Transitioning to the Method-Ready Pool
@Stateless
public class MyBean implements MyLocal {
@PostConstruct
public void myInit( ) {}

Alternatively, you can declare your @PostConstruct method in
the EJB's XML deployment descriptor:
<ejb-jar>
<enterprise-beans>
<session>
<ejb-name>MyBean</ejb-name>
<post-construct>
<lifecycle-callback-method>myInit</lifecycle-callback-method>
</post-construct>
</session>
</enterprise-beans>
</ejb-jar>
40
Life in the Method-Ready Pool





Once an instance is in the Method-Ready Pool, it is
ready to service client requests.
When a client invokes a business method on an EJB
object, the method call is delegated to any available
instance in the Method-Ready Pool.
While the instance is executing the request, it is
unavailable for use by other EJB objects.
Once the instance has finished, it is immediately
available to any EJB object that needs it.
Stateless session instances are dedicated to an EJB
object only for the duration of a single method call.
41
Transitioning out of the Method-Ready Pool:
the death of a stateless bean instance




Bean instances leave the Method-Ready Pool for the Does
Not Exist state when the server no longer needs them - that is,
when the server decides to reduce the total size of the
Method-Ready Pool by evicting one or more instances from
memory.
The process begins when a PreDestroy event on the bean is
triggered. The bean class can register for this event by
annotating a method with @javax.annotation.PreDestroy .
The container calls this annotated method when the
PreDestroy event is fired. This callback method can be of any
name, but it must return void , have no parameters, and throw
no checked exceptions.
The bean class may define only one @PreDestroy method
(but it is not required to do so).
42
The Stateful Session Bean




Each stateful session bean is dedicated to one client for the
life of the bean instance; it acts on behalf of that client as its
agent.
Stateful session beans are not swapped among EJB
objects nor are they kept in an instance pool, like stateless
session bean instances are.
Once a stateful session bean is instantiated and assigned
to an EJB object, it is dedicated to that EJB object for its
entire life cycle.
Stateful session beans maintain conversational state, which
means that the instance variables of the bean class can
maintain data specific to the client between method
invocations.
43
The Stateful Session Bean



Stateful session beans allow you to encapsulate
some of the business logic and conversational state
of a client and move it to the server.
Moving this logic to the server thins the client
application and makes the system as a whole easier
to manage.
The stateful session bean acts as an agent for the
client, managing processes or taskflow to accomplish
a set of tasks; it manages the interactions of other
beans in addition to direct data access over several
operations to accomplish a complex set of tasks.
44
Getting Set Up for the TravelAgent EJB

The TravelAgent EJB will use the Cabin, Cruise,
Reservation, and Customer entity beans that we have
devloped earlier. It will coordinate the interaction of these
entity beans to book a passenger on a cruise. We'll modify
the Reservation entity so that it can be created with all of its
relationships identified right away. In other words, we will
define another constructor in addition to a default
constructor:
public class Reservation {
public Reservation( ) {}
public Reservation(Customer customer, Cruise cruise,
Cabin cabin, double price, Date dateBooked) {
setAmountPaid(price);
setDate(dateBooked);
setCruise(cruise);
45
Getting Set Up for the TravelAgent EJB
Set cabins = new HashSet( );
cabins.add(cabin);
this.setCabins(cabins);
Set customers = new HashSet( );
customers.add(customer);
this.setCustomers(customers);
}

Creating this constructor will allow us to avoid calling all of
those setter methods within our TravelAgent EJB code and
will make it less cluttered.
46
The TravelAgent EJB



The TravelAgent EJB, which we have already seen, is a
stateful session bean that encapsulates the process of
making a reservation on a cruise.
We will develop this bean further to demonstrate how
stateful session beans can be used as taskflow objects.
We won't develop a local interface for the TravelAgent
EJB, partly because it is designed to be used by remote
clients (and therefore doesn't require local component
interfaces), and partly because the rules for developing
local interfaces for stateful session beans are the same
as those for stateless session beans.
47
The remote interface: TravelAgent


Here's the modified TravelAgentRemote interface:
package com.titan.travelagent;
import com.titan.processpayment.CreditCardDO;
import javax.ejb.Remote;
import com.titan.domain.Customer;
@Remote
public interface TravelAgentRemote {
public Customer findOrCreateCustomer(String first, String last);
public void updateAddress(Address addr);
public void setCruiseID(int cruise);
public void setCabinID(int cabin);
public TicketDO bookPassage(CreditCardDO card, double price)
throws IncompleteConversationalState;
}
48
The remote interface: TravelAgent

The IncompleteConversationalState exception indicates that the
TravelAgent EJB did not have enough information to process the
booking. Here's the IncompleteConversationalState class:
package com.titan.travelagent;
public class IncompleteConversationalState extends
java.lang.Exception
{
public IncompleteConversationalState( ){super( );
}
public IncompleteConversationalState(String msg)
{
super(msg);
}
}
49
Domain objects: the TicketDO class





Like the CreditCardDO and CheckDO classes used in the
ProcessPayment EJB, the TicketDO class is defined as a
pass-by-value object.
One could argue that a ticket should be the Reservation
entity bean, since it is a plain Java object and could be
serialized back to the client.
However, determining how a business object is used can
also dictate whether it should be a bean or simply a class.
Because the Reservation entity bean references a lot of
interrelated entities, the number of objects serialized back to
the client could become quite large, and thus very inefficient.
With the TicketDO object, you can pull together the exact
information you want to send back to the client.
50
Domain objects: the TicketDO class

The constructor for TicketDO uses the entities from which it
pulls the data:
package com.titan.travelagent;
import com.titan.domain.Cruise;
import com.titan.domain.Cabin;
import com.titan.domain.Customer;
public class TicketDO implements java.io.Serializable {
public int customerID;
public int cruiseID;
public int cabinID;
public double price;
public String description;
51
Domain objects: the TicketDO class
public TicketDO(Customer customer, Cruise cruise,
Cabin cabin, double price) {
description = customer.getFirstName( )+
" " + customer.getLastName( ) +
" has been booked for the "
+ cruise.getName( ) +
" cruise on ship " +
cruise.getShip( ).getName( ) + ".\n" +
" Your accommodations include " +
cabin.getName( ) +
" a " + cabin.getBedCount( ) +
" bed cabin on deck level " + cabin.getDeckLevel( ) +
".\n Total charge = " + price;
customerID = customer.getId( );
cruiseID = cruise.getId( );
cabinID = cabin.getId( );
this.price = price;
}
public String toString( ) {
return description;
}
}
52
The bean class: TravelAgentBean

We can now implement all of the behavior expressed in the
new remote interface for the TravelAgent EJB. Here is a
partial definition of the new TravelAgentBean class:
package com.titan.travelagent;
import
import
import
import
import
import
com.titan.processpayment.*;
com.titan.domain.*;
javax.ejb.*;
javax.persistence.*;
javax.annotation.EJB;
java.util.Date;
@Stateful
public class TravelAgentBean implements TravelAgentRemote {
@PersistenceContext(unitName="titan")
private EntityManager entityManager;
@EJB private ProcessPaymentLocal processPayment;
53
The bean class: TravelAgentBean
private Customer customer;
private Cruise cruise;
private Cabin cabin;
public Customer findOrCreateCustomer(String first, String last) {
try {
Query q = entityManager.createQuery("from Customer c where
c.firstName = :first and c.lastName = :last");
q.setParameter("first", first);
q.setParameter("last", last);
this.customer = (Customer)q.getSingleResult( );
} catch (NoResultException notFound) {
this.customer = new Customer( );
this.customer.setFirstName(first);
this.customer.setLastName(last);
entityManager.persist(this.customer);
}
return this.customer;
}
public void updateAddress(Address addr) {
this.customer.setAddress(addr);
this.customer = entityManager.merge(customer);
}
54
The bean class: TravelAgentBean
public void setCabinID(int cabinID) {
this.cabin = entityManager.find(Cabin.class, cabinID);
if (cabin == null) throw new NoResultException("Cabin not
found");
}
public void setCruiseID(int cruiseID) {
this.cruise = entityManager.find(Cruise.class, cruiseID);
if (cruise == null) throw new NoResultException("Cruise not
found");
}
@Remove
public TicketDO bookPassage(CreditCardDO card, double price)
throws IncompleteConversationalState {
if (customer == null || cruise == null || cabin == null)
{
throw new IncompleteConversationalState( );
}
55
The bean class: TravelAgentBean
try {
Reservation reservation = new Reservation(
customer, cruise, cabin, price, new Date( ));
entityManager.persist(reservation);
processPayment.byCredit(customer, card, price);
TicketDO ticket = new TicketDO(customer, cruise, cabin,
price);
return ticket;
}
catch(Exception e) {
throw new EJBException(e);
}
}
}
56
The bookPassage( ) method

The last point of interest in our bean definition is the
bookPassage( ) method. This method uses the
conversational state accumulated by the
findOrCreateCustomer( ), setCabinID( ), and setCruiseID( )
methods to process a reservation for a customer. Here's how
the bookPassage( ) method is defined:
@Remove
public TicketDO
bookPassage(CreditCardDO card, double price) throws IncompleteConversationalState
{
if (customer == null || cruise == null || cabin == null)
{
throw new IncompleteConversationalState( );
}
try {
Reservation reservation = new Reservation(
customer, cruise, cabin, price, new Date( ));
entityManager.persist(reservation);
process.byCredit(customer, card, price);
TicketDO ticket = new TicketDO(customer, cruise, cabin, price);
return ticket;
} catch(Exception e) {
throw new EJBException(e);
}
57
}
The bookPassage( ) method




This method demonstrates the taskflow concept.
It uses several beans, including the ProcessPayment
EJB and the Reservation, Customer, Cabin, and Cruise
entities, to accomplish one task: booking a customer on
a cruise.
Deceptively simple, this method encapsulates several
interactions that ordinarily might have been performed
on the client.
For the price of one bookPassage( ) call from the client,
the TravelAgent EJB performs the following operations,
in this order:
58
The bookPassage( ) method






Creates a new Reservation object
Persists the new Reservation object with the entity manager
service
Charges the customer's credit card using the
ProcessPayment EJB
Generates a new TicketDO with all the pertinent information
describing the customer's purchase
Notice that the bookPassage( ) method is annotated with the
@javax.ejb.Remove annotation.
The @Remove annotation tells the EJB container that when
the method completes, the client no longer needs the
session. After the bookPassage( ) method completes, the
EJB container removes the session from the EJB container.
59
The XML Deployment Descriptor

Here is a deployment descriptor that provides a complete
annotation-alternative definition of the TravelAgent EJB:
<?xml version="1.0"?>
<ejb-jar
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd"
version="3.0">
<enterprise-beans>
<session>
<ejb-name>TravelAgentBean</ejb-name>
<remote>com.titan.travelagent.TravelAgentRemote</remote>
<ejb-class>com.titan.travelagent.TravelAgentBean</ejb-class>
<session-type>Stateful
</session-type>
<ejb-local-ref>
<ejb-ref-name>ejb/PaymentProcessor</ejb-ref-name>
<ejb-ref-type>Session</ejb-ref-type>
<local>com.titan.processpayment.ProcessPaymentLocal</local>
60
The XML Deployment Descriptor
<injection-target>
<injection-target-class>
com.titan.travelagent.TravelAgentBean
</injection-target-class>
<injection-target-name>processPayment
</injection-target-name>
</injection-target>
</ejb-local-ref>
<persistence-context-ref>
<persistence-context-ref-name>
persistence/titan
</persistence-context-ref-name>
<persistence-unit-name>titan</persistence-unit-name>
<injection-target>
<injection-target-class>
com.titan.travelagent.TravelAgentBean
</injection-target-class>
<injection-target-name>entityManager</injection-target-name>
</injection-target>
</persistence-context-ref>
</session>
</enterprise-beans>
</ejb-jar>
61
The Life Cycle of a Stateful Session Bean





The biggest difference between the stateful session bean and
the other bean types is that stateful session beans do not use
instance pooling.
Stateful session beans are dedicated to one client for their
entire lives, so swapping or pooling of instances isn't
possible.
Some vendors use pooling with stateful session beans, but
that is a proprietary implementation and should not affect the
specified life cycle of the stateful session bean.
The life cycle of a stateful session bean has three states:
Does Not Exist, Method-Ready, and Passivated.
This sounds a lot like a stateless session bean, but the
Method-Ready state is significantly different from the MethodReady Pool of stateless beans.
62
The Life Cycle of a Stateful Session Bean
Stateful session bean life cycle
63
The Does Not Exist State
A stateful bean instance in the Does Not
Exist state has not been instantiated yet.
 It doesn't exist in the system's memory.

64
The Method-Ready State
The Method-Ready state is the state in which
the bean instance can service requests from
its clients.
 This section explores the instance's transition
into and out of the Method-Ready state.

65
Transitioning into the Method-Ready
state





When a client invokes the first method on the stateful
session bean reference, the bean's life cycle begins.
The container invokes newInstance( ) on the bean class,
creating a new instance of the bean.
Next, the container injects any dependencies into the bean
instance. At this point, the bean instance is assigned to the
client referencing it.
Finally, just like stateless session beans, the container
invokes any @PostConstruct callbacks if there is a method
in the bean class that has this annotation applied.
Once @PostConstruct has completed, the container
continues with the actual method call.
66
Life in the Method-Ready state


While in the Method-Ready state, the bean
instance is free to receive method invocations
from the client, which may involve controlling the
taskflow of other beans or accessing the
database directly.
During this time, the bean can maintain
conversational state and open resources in its
instance variables.
67
Transitioning out of the Method-Ready state



Bean instances leave the Method-Ready state to enter
either the Passivated state or the Does Not Exist state.
Depending on how the client uses the stateful bean, the
EJB container's load, and the passivation algorithm used
by the vendor, a bean instance may be passivated (and
activated) several times in its life, or not at all.
If the bean is removed, it enters the Does Not Exist state.
A client application can remove a bean by invoking a
business interface method annotated as @Remove .
68
The Passivated State



During the lifetime of a stateful session bean, there
may be periods of inactivity when the bean instance
is not servicing methods from the client.
To conserve resources, the container can passivate
the bean instance by preserving its conversational
state and evicting the bean instance from memory.
A bean's conversational state may consist of
primitive values, objects that are serializable, and
the following special types:
69
The Passivated State








javax.ejb.SessionContext
javax.jta.UserTransaction (bean transaction interface)
javax.naming.Context (only when it references the JNDI
ENC)
javax.persistence.EntityManager
javax.persistence.EntityManagerFactory
References to managed resource factories (e.g.,
javax.sql.DataSource )
References to other EJBs
The activation of a bean instance follows the rules of
Java serialization, regardless of how the bean's state
was actually stored.
70
System exceptions


Whenever a system exception is thrown by a
bean method, the container invalidates the
EJB object and destroys the bean instance.
The bean instance moves directly to the Does
Not Exist state, and any @PreDestroy call
methods are not invoked.
71
Nested Stateful Session Beans

Stateful session beans behave in interesting ways when you
inject other stateful session beans within them. When you
inject a stateful session bean into an EJB with the @EJB
annotation, a session is created for that injected instance:
@Stateful
public class ShoppingCartBean
implements ShoppingCart{
@EJB AnotherStatefulLocal
another;
@Remove void checkout {}
}

When a stateful bean reference is injected into another
stateful session bean, the containing bean owns the injected
session.
72
Nested Stateful Session Beans




This means that when the containing bean is created, a
unique session is created for the injected reference.
When the containing bean is removed, the contained
stateful bean is also removed. In the ShoppingCartBean
example, an AnotherStatefulLocal session is created for the
another member variable when an instance of it is created.
When the checkout( ) method is called, another's stateful
session is also removed.
This feature of the specification allows you to cleanly
aggregate conversational business processes without
having to worry about managing the life cycle of the
contained stateful sessions.
73