Session Beans
Session beans model high-level business processes, session beans utilize data and system resources to implement the application goals using business logic. In this topic I will go into details regarding session beans, continuing from my introduction on session beans in my EJB getting started topic.
Session beans capture the logic such as creating users, add items for an auction, bidding for an item, etc. A session is a connection between a client and a server that lasts for a finite period of time, it can be short-lived or span a long time. Session beans are the only EJB component that are invoked directly by the client, the client could be a web application component (servlet, JSP, JSF, etc), a commandline application or a Swing GUI application.
The following is what session beans can offer
Concurrency and Thread Safety | The container will manage concurrency and thread safety automatically by using pooling, session management and passivation. |
Remoting and Web Services | Session beans support Remote Method Invocation (RMI) and Simple Object Access Protocol (SOAP) remote access which enables distributed computing and interoperability. |
Transaction and Security Management | Session beans offer fully transaction management and authorization and authentication security |
Timer Services and Interceptors | Interceptors are EJB's version of lightweight Aspect-Oriented Programming (AOP), which has the ability to isolate "crosscutting" concerns into their own modules and apply them across the application through configuration, things like logging, auditing are repeated across an application but are not part of the business logic. Timer services offer a "cron" type of scheduling. |
The alternative option is to use Spring, this has advantages and disadvantages and could also be used in conjunction with EJB 3.
Each session bean implementation has two distinct parts
To access a bean you must implement a business interface and interface-based programming is the practice of not using implementation classes directly whenever possible. This approach promotes loose coupling since implementation classes can easy be swapped out without alot of code changes.
The interface through which a client accesses the bean is called a business interface, the interface could be local, remote or even a web service, which means the bean can be polymorphic. Session beans are POJO's and since annotations are ignored by the JVM, session beans can be unit-tested using a framework like JUnit without having to deploy them to a container.
Session beans follow a number of rules
If you do not already know there are two types of session beans, stateless and stateful, the reason for this is if you require to save conversational information (like a shopping cart) between requests, thus a stateful bean will remember conversations between the client and the bean, where as stateless bean do not save conversation state.
A session has a lifecycle, it goes through a predefined set of state transitions, what this means is that the bean is a managed resource and its the Container who is the manager. It controls when the bean instance is created, when dependencies are injected, when the instance is destroyed or when to take optimization measures. By allowing the Container to take control it provides the abstraction that constitute some of the real value of using EJB's, including DI, automated transaction management, AOP, transparent security.
Depending on the bean type stateless or stateful a number of key lifecycle events can occur
Lifecycle callbacks are bean methods (not exposed by the business interface) that the container calls to notify the bean about a lifecycle transition or event. When an event occurs the Container can perform an action like initialization or cleanup resources. The callback methods are called using annotations like below
Annotation | Type of EJB | What used for |
@PostConstruct | Stateless, Stateful, MDB | Invoke just after the bean instance is created and DI is complete |
@PreDestroy | Stateless, Stateful, MDB | Invoked just before the bean is destroyed |
@PrePassivate | Stateful | Invoke prior to a bean instance being passivated |
@PostActivate | Stateful | Invoked after the bean instance is activated. |
Stateless beans do not maintain conversation, and you normally use them to complete a business function is a single method call. Stateless beans are pooled which mean they are efficient, the container keeps a certain number of instances in the pool which are defined by you. Once the client has finished with an instance it is returned back to the pool for reuse.
A typical bean example might be the following, try not to worry about the application but focus on what we have discussed above (highlighted) and how use it below, I have also avoided using JPA for the time being as I don't want to make the code to heavy, so I will use JDBC instead, however I will have a number of topics dedicated to JPA.
BidManager Interface | @Remote public interface BidManager { void addBid(Bid bid); void cancelBid(Bid bid); List<Bid> getBids(Item item); } |
stateless bean | @Stateless(name="BidManager") public class BidManagerBean implements BidManager { @Resource(name="jdbc/ActionBazzarDS") private Datasource datasource; private Connection connection; ... // The no-argument constructor required by the container public BidManagerBean() {} @PostConstruct public void initialize() { try { connection = datasource.getConnection(); } catch (SQLException sqle) { sqle.printStackTrace(); } } public void addBid(Bid bid) { try { Long bidId = getBidId(); Statement statement = connection.createStatement(); statement.execute( "INSERT INTO BIDS (" + "BID_ID, " + "BID_AMOUNT, " + "BID_BIDDER_ID, " + "BID_ITEM_ID) " + "VALUES (" + bidId + ", " + bid.getAmount() + ", " + bid.getBidder().getUserId() + ", " + bid.getItem().getItemId() + ")"); } catch (Exception e) { e.printStackTrace(); } } @PreDestroy public void cleanup() { try { connection.close(); } catch (SQLException sqle) { sqle.printStackTrace(); } private Long getBid() { ... } public void cancelBid(Bid Bid) { ... } public List<Bid> getBids(Item item) { ... } } } |
We program to an interface which is marked as remote (@Remote) , we declare the bean to be a stateless bean (@Stateless) and we use the resource annotation (@Resource) to perform injection of a JDBC data source. Notice the no-argument constructor which the container will use to create an instance of this EJB object. We have also included two callbacks @PostConstruct and @PreDestory to manage the JDBC connection. There are a couple of other methods that perform certain tasks. Note that you can have multiple @PostConstruct and @PreDestroy callbacks within the bean, they also cannot throw check exceptions.
The @Stateless annotation marks the POJO as a stateless session bean, this makes the container aware of the beans type, the specification of this annotation is
@Stateless annotation | @Target(TYPE) @Retension(RUNTIME) public @interface Stateless { String name() default ""; String mappedName() default ""; String description() default ""; } |
You can give your bean a name that the container will use to reference it by binding it to the global JNDI tree, if no name is given it uses the class name.
Client applications can invoke a session bean in 3 different ways
Local interface |
@Local |
is designed for clients of stateless session beans collocated in the same container (JVM) instance. Local interfaces do not require any special measures in terms of either defining or implementing them |
Remote interface | @Remote |
Clients residing outside the container (another JVM) must use some kind of remote interface. The best way to access a remote EJB object is using Remote-Method Invocation (RMI), this API allows you call a method of a stateless bean across the network. The only requirement of a remote stateless bean is that all parameters and return types of interface methods must be serializable. This is because only serializable objects can be sent across the network using RMI. You can optionally extend the bean using the java.rmi.Remote, by extending you can then throw java.rmi.RemoteException |
Web Services interface | @WebService |
The web service endpoint interface (SEI) gives you the ability to expose a stateless bean as a SOAP-based web service. They can only be used by stateless session beans. |
You cannot mark the same interface with more than one access type annotation, however a business interface can extend another interface and you can remove code duplication by creating a business interface that has common methods and business interfaces that extend the common parent interface.
Implementing more than interface | public interface BidManager { @Remote @WebService @Remote(BidManager.class) |
Stateless session beans have a simple lifecycle and the Container performs the following:
The above allows session beans to handle a large number of virtually concurrently clients.
Stateful beans are guaranteed to maintain conversational state, they are not programmatically very different from their stateless cousins. The container will make sure that the same client are handled by the same stateful bean bean instance. Stateful beans are not pulled from a pool, instead they are created and live until either requested to be removed or timeout. This has the affect that stateful beans can have significant footprint, however there is a technique that can be used to combat this problem called passivation.
Stateful session beans have additional rules
A stateful bean would be used if multiple data requires collection from the client, the user would enter username/password information, then biographical data (name, address, etc), then billing info after all the data has been collected would could create an account. The information collected would be stored in instance variables.
Interface | @Remote void addBiographicalInfo(BiographicalInfo biographicalInfo) throws WorkflowOrderViolationException; void addBillingInfo(BillingInfo billingInfo) throws WorkflowOrderViolationException; void cancelAccountCreation(); void createAccount(); |
stateful session bean example | @Stateful(name = "BidderAccountCreator") @Resource(name = "jdbc/ActionBazaarDS", mappedName = "java:/DefaultDS") // obtain resource connections just after being created or being activated // These methods are used to obtain the conversation data and stored in instance variables public void addBiographicalInfo(BiographicalInfo biographicalInfo) this.biographicalInfo = biographicalInfo; public void addBillingInfo(BillingInfo billingInfo) this.billingInfo = billingInfo; @Remove @Remove // cleanup before either being destroyed or passivated |
Stateful session beans have a more complex lifecycle and the Container performs the following:
Stateful and Stateless Differences
The table below details the differences between stateless and stateful beans
Features |
Stateless |
Stateful |
Conversational state | No |
Yes |
Pooling | Yes |
No |
Performance problems | Unlikely |
Possible |
Lifecycle Events | PostConstruct PreDestroy |
PostConstruct PreDestroy PrePassivate PostActivate |
Timer | Yes |
No |
SessionSynchronization for transactions | No |
Yes |
Web Services | Yes |
No |
Extended PersistenceContext | No |
Yes |
A session works for a client and can be invoked locally (inside the JVM) or remotely (outside the JVM), almost any Java component (POJO's, servlets, JSPs, EJBs) including non-Java components like .Net can access session beans. The client generally uses the below steps to access a session bean
You can inject references to other beans using the @EJB annotation, you may have to use one of the two other options available for obtaining EJB references using either EJB context lookup or using JNDI lookup.
@EJB annotation | @Target({TYPE, METHOD, FIELD}) @Retention(RUNTIME) name = the JNDI name that is used to bind the injected EJB |
@EJB example | @Stateless public class GoldBidderManagerBean implements GoldBidderManager { // use the remote interface @EJB private BidManager bidManager; // using the local interface // @EJB(name="BidManagerLocal") // private BidManagerLocal bidManager; public void addMassBids(List<Bid> bids) { for (Bid bid : bids) { bidManager.addBid(bid); } } } |
You can inject a stateless bean into a stateful bean but you cannot inject a stateful bean into a stateless bean.
Deciding whether you maintain conversational state helps in deciding to choose either stateless or stateful session beans, remember that stateless beans are more efficient and that stateful beans can consume vast amounts of memory if not removed properly. Make sure that if using stateful session beans that you limit the amount of conversation data you store, just store what you need. Also bear in mind if using a cluster that stateful beans are replicated and use network bandwidth.
Certain containers allow you to adjust the passivation policies that can determine how long before to passivate a idle stateful bean, or when a JVM threshold has been reached. Remember to use the @Remove annotation to help the container remove the bean rather than letting it timeout.
Here are some more general tips