Ocpjbcd 6 Study Notes
Ocpjbcd 6 Study Notes
Ocpjbcd 6 Study Notes
Study Notes
by
Ivan A Krizsan
Version: November 25, 2013
Table of Contents
Table of Contents................................................................................................................................. 2
Purpose ............................................................................................................................................... 9
Structure.............................................................................................................................................. 9
Licensing ............................................................................................................................................ 9
Disclaimers ......................................................................................................................................... 9
Prerequisites...................................................................................................................................... 10
Thanks!.............................................................................................................................................. 10
1. Plan the Development of a Business Service Model...................................................................... 12
1.1.Reasons for Using EJBs........................................................................................................... 12
1.2.Reasons for Not Using EJBs.................................................................................................... 12
1.3.EJB Programing Restrictions................................................................................................... 13
1.4.EJB 3.1 Lite vs Full EJB 3.1 API............................................................................................ 15
1.5.Choosing the Type of EJB....................................................................................................... 16
1.6.Session Beans - Stateful, Stateless or Singleton?.................................................................... 16
1.6.1.Stateful Session Beans..................................................................................................... 16
1.6.2.Stateless Session Beans.................................................................................................... 17
1.6.3.Singleton Session Beans.................................................................................................. 17
1.7.Designing the Client View of Session Beans........................................................................... 17
1.8.Remote or Local Access of Session Beans.............................................................................. 18
1.8.1.Choosing Client Access Type........................................................................................... 18
1.8.2.Access Type and Parameters............................................................................................ 19
1.9.Message Driven Beans.............................................................................................................20
1.9.1.Queue or Topic................................................................................................................. 20
2. Create a Stateful Session Bean....................................................................................................... 22
2.1.Create a Stateful Session Bean with a Local No-interface View ............................................. 22
2.2.Create a Stateful Session Bean with a Local Business Interface ............................................. 28
2.3.Create a Stateful Session Bean with a Remote Business Interface ..........................................30
2.4.Create a Stateful Session Bean without Annotations ............................................................... 32
2.5.Stateful Session Bean Life-Cycle............................................................................................ 34
2.6.Operations Allowed in Methods of a Stateful Session Bean................................................... 35
3. Create a Stateless Session Bean..................................................................................................... 37
3.1.Create a Stateless Session Bean with a Local No-interface View ...........................................37
3.2.Create a Stateless Session Bean with a Local Business Interface ...........................................40
3.3.Create a Stateless Session Bean with a Remote Business Interface ........................................ 42
3.4.Create a Stateless Session Bean without Annotations ............................................................. 44
3.5.Stateless Session Bean Life-Cycle........................................................................................... 46
3.6.Operations Allowed in Methods of a Stateless Session Bean.................................................. 47
4. Create a Singleton Session Bean.................................................................................................... 50
4.1.Basic Singleton Session Bean Example...................................................................................51
4.2.Singleton Session Bean Initialization...................................................................................... 55
4.2.1.Singleton Session Bean Eager Initialization .................................................................... 55
4.3.Singleton Session Bean Destruction........................................................................................ 56
4.4.Singleton Session Bean Initialization and Destruction Dependencies ....................................57
4.5.Transactions and Singleton Session Bean Creation and Destruction ......................................58
4.6.Singleton Session Bean Concurrency...................................................................................... 59
4.6.1.Basic Singleton Session Bean Concurrency Example..................................................... 59
4.6.2.Concurrency Management............................................................................................... 64
The @Lock Annotation................................................................................................... 65
2
Purpose
These are the notes I made during the preparations for the OCP Java EE 6 EJB Developer
certification. The focus of this book is development of Enterprise Java Beans according to the EJB
3.1 (JSR-318) standard.
While it is possible to use, for instance, enterprise Java beans adhering to the EJB 2.1 standard in an
EJB 3.1 container, I have deemed this as being out of the scope of this book. Also, the use of JAXRPC in connection to EJBs has not been considered.
Structure
The structure of this book is as follows:
Chapters 1 to 13 maps to the certification objectives.
Chapters 14 to 17 contains common information that, for instance, applies to multiple types
of enterprise beans.
The appendices contain examples related to development of EJBs.
For instance, how to set up the Eclipse development environment, how to configure
different aspects of the GlassFish application server and additional examples.
Licensing
This book is licensed under a Creative Commons Attribution-Noncommercial-No Derivative Works
3.0 license. In short this means that:
You may share this book with others.
You may not use this book for commercial purposes.
You may not create derivate works from this book.
Disclaimers
Though I have done my best to avoid it, this book might contain errors. I cannot be held
responsible for any effects caused, directly or indirectly, by the information in this book you
are using it on your own risk.
I cannot make any guarantees concerning the completeness of the information contained in this
book if you are preparing for the certification, please do consult additional sources.
Submitting any suggestions, or similar, the information submitted becomes my property and you
give me the right to use the information in whatever way I find suitable, without compensating you
in any way.
All trademarks are properties of their respective owner and do not imply endorsement of any kind.
This book has been written in my spare time and has no connection whatsoever with my
employer.
This book was written prior to Oracle listing detailed information about the OCPEJBD 6
certification. This means that this book may not entirely cover all the certification objectives.
Prerequisites
When developing the examples in this book, I have been using the GlassFish v3 application server
and Eclipse Helios as the IDE.
The version of Eclipse I worked with did not find a GlassFish plugin when adding a new server,
instead I had to install the GlassFish plugin using the Install New Software... menu alternative in
Eclipse using the following update site: http://download.java.net/glassfish/eclipse/helios
Thanks!
Thanks to the persons having given me feedback on this book and pointed out mistakes I made!
I am very grateful for this invaluable help I have received enabling me to make this book better.
Listed in no particular order:
Markos Fragkakis
Frits Walraven
Luay Abdulraheem
10
Part One
Certification Topics
11
Scalability is required.
EJBs can be distributed across multiple computers while their location remain transparent to
clients.
Transactions are required.
Different kinds of clients will access the application.
EJBs can be accessed in multiple ways; local or remote EJBs, SOAP or REST web service.
Management of concurrency is desired.
The EJB container can manage concurrent access to an EJB, allowing only a single item at a
time to use the EJB.
Security is required.
EJBs offer a security model that enables security to be configured declaratively, without
affecting the implementation. Programmatic security is also available.
Portability across different application servers is desired.
Distributed deployment: If required, there must be a remote view of the EJB or the EJB must be
exposed as a web service, either RESTful or SOAP. An EJB can also be exposed as a message
consumer.
Exposing an EJB as a web service does not require any modifications to the code implementing the
business logic in the EJB.
We will look closer at transactions with EJBs in chapter 10 and security with EJBs in chapter 11.
If the full EJB API is considered too heavyweight, there is still the EJB Lite alternative, which is a
subset of the EJB 3.1 API. EJB Lite still requires an EJB container.
There is also an embeddable EJB 3.1 container, which is only required to support the EJB Lite
subset of the EJB 3.1 API. The embeddable container enables use of the EJB programming model
in, for instance, desktop applications.
12
Motivation
An EJB must not attempt to load a native library. Could compromise security.
An EJB must not attempt to circumvent the rules Could compromise security.
of the Java programming language when
accessing packages and classes.
An EJB must not attempt to define a class in a
package.
13
14
Available
Available
Available
Not available
Not available
Not available
Not available
Available
Not available
Asynchronous invocation of
session beans.
Available
Not available
Interceptors.
Available
Available
RMI-IIOP interoperability.
Available
Not available
Available
Available
Available
Available
Embeddable API.
Available
Available
Not available
15
Session beans.
Message-driven beans.
Session beans are components that contains business logic that can be invoked over local, remote or
web service client views.
Message-driven beans are components that, upon receiving an asynchronous message, executes
some business logic. Usually message-driven beans listen for JMS messages.
Different reasons for choosing to use session-driven beans are discussed in the next section.
Reasons for using message-driven beans are discussed in a subsequent section.
The EJB needs to retain state related to a client across multiple method invocations.
The EJB mediates between a client and other components of the application, presenting a
simplified view to the client.
Behind the scenes, the EJB manages the workflow of several EJBs.
16
The EJB performs one or more generic tasks, for instance sending an email, that can be
finished during one single method invocation.
Singleton session beans can be configured to allow multiple threads to execute in an instance
concurrently. Under such circumstances, special care have to be taken when designing the singleton
session bean in order to, for instance, protect resources that must not be accessed concurrently.
For more details, please refer to chapter four.
17
Local Access
The session beans is accessed from within the same application.
Possible clients are web components and other EJBs.
Remote Access
The session bean is accessed from the same or from another JVM. The JVM in which the
client executes may be deployed on a different computer.
Note that remote access of an EJB also is allowed for a client running in the same JVM as
the EJB.
Possible clients are web components, other EJBs and application clients.
It should be noted that local access does not exclude remote access of an EJB. An EJB may allow
both local and remote access.
18
Whether to select local or remote access of an EJB may depend on one or more of the following
factors:
Tight or loose coupling between an EJB and its clients / EJB operation granularity.
If the client closely depends on the EJB to perform its duties and often invoke the EJB, then
it is more tightly coupled to the EJB and will benefit from local access, which will improve
performance.
Type of clients.
If the EJB is access by application clients, then it needs to provide remote access, since such
clients almost always run on other machines.
Web components and other EJBs may access the EJB locally or remotely, depending on how
the application is distributed.
Component distribution.
When using JavaEE, server-side components may be distributed on several different nodes
to facilitate better scalability. When distributing web component on one machine and the
EJBs implementing the business functionality on another machine, the EJBs need to provide
remote access.
Location independence.
An EJB providing a remote view can be deployed in the same container as the client or on a
remote container, while an EJB providing only a local view are only accessible by clients in
the same application as the EJB. An EJB with a remote view is more flexible in that it may
be moved as desired.
Parameters and return values must be serializable for EJBs with a remote view.
If passing large amounts of data between an EJB and its clients, serialization of the data may
cause overhead. With local access to an EJB, parameters are passed by reference and
serialization overhead is thus avoided the EJB can operate on the data and the client,
holding a reference to the same data, will see the modifications.
Performance
Remote calls of EJBs always incur a performance penalty, since it means access over some
network. However, distribution of components on several nodes may improve the overall
performance of the system. Measuring the performance of a system in different
configurations is the best way of learning about the characteristics of a particular
application.
19
The container can assign any instance of an EJB to serve a client request.
Instances may be pooled by the container.
1.9.1.Queue or Topic
There are two different messaging models that can be employed by queues that message driven
beans are listening to; queue or topic.
The queue messaging model is also referred to as the point-to-point messaging model, while the
topic messaging model is also referred to as the publish-and-subscribe or pub/sub messaging model.
Common for both these messaging models are:
Messages from producer to consumer(s) are exchanged through virtual channels.
The type of virtual channel depends on the messaging model.
Producers and consumers are decoupled.
Producers and consumers of messages does not know about each other, apart from the fact
that there are messages from the former delivered to the latter.
Producers and consumers of messages can be dynamically added at runtime.
Messages are pushed to message driven beans regardless of the messaging model.
Regular JMS message consumers can pull messages from a queue but message driven beans
does not have this option.
Characteristic of the point-to-point messaging model (queues) are:
The virtual channel used to exchange messages is called a queue.
A message sent to a queue is delivered to exactly one consumer.
Messages are delivered in the order in which they were produced.
Using different message priorities may affect the order in which messages are delivered.
Messages sent to a queue can be persistent or non-persistent.
Persistent messages are retained in the case of JMS provider failures.
A message sent to a queue may be assigned a priority.
20
Queues are suitable when an application want to, for instance, divide work among a number of
workers - the workers all listen to the queue but each message is only received by one of the
workers. The producer knows that the unit of work will eventually be received and processed by
one of the workers.
Queues are also suitable for reliable asynchronous communication between two parties; the first
party sends messages to a queue which are later delivered to the other party. The other party replies
by sending another message to another queue, which the first party is a consumer of.
When To Use Topics
Topics are suitable when multiple consumers are to receive a copy of each message produced.
Commonly topics are used when the producer of some information want to give the opportunity of
interested parties to receive the information. The producer commonly does not care about who
receives the information.
A commonly used example of when to use a topic is in a stock quote application.
21
In the package com.ivan.scbcd6, create the session bean implementation class implemented
as this:
package com.ivan.scbcd6;
import
import
import
import
import
import
import
import
import
import
java.util.Date;
java.util.concurrent.TimeUnit;
javax.annotation.PostConstruct;
javax.annotation.PreDestroy;
javax.ejb.LocalBean;
javax.ejb.Remove;
javax.ejb.Stateful;
javax.ejb.StatefulTimeout;
javax.ejb.TransactionAttribute;
javax.ejb.TransactionAttributeType;
/**
* Simplest possible stateful session bean exposing a local, no-interface view.
*/
@Stateful
@LocalBean
@StatefulTimeout(value=10, unit=TimeUnit.SECONDS)
@TransactionAttribute(TransactionAttributeType.NEVER)
public class StatefulSession1Bean
{
private static int sCurrentInstanceNumber = 1;
private int mInstanceNumber;
@PostConstruct
public void initialize()
{
mInstanceNumber = sCurrentInstanceNumber++;
System.out.println("*** StatefulSession1Bean " + mInstanceNumber +
" created.");
}
@PreDestroy
public void destroy()
{
System.out.println("*** StatefulSession1Bean " + mInstanceNumber +
" destroyed.");
22
}
@Remove
public void remove()
{
System.out.println("*** StatefulSession1Bean " + mInstanceNumber +
" remove.");
}
/**
* Creates a greeting to the person with the supplied name.
*
* @param inName Name of person to greet.
* @return Greeting.
*/
public String greeting(final String inName)
{
Date theCurrentTime = new Date();
String theMessage = "Hello " + inName + ", I am stateful session bean " +
mInstanceNumber + ". The time is now: " + theCurrentTime;
return theMessage;
}
}
Create the package com.ivan.scbcd6.client and in it, create the client servlet implemented as
follows:
package com.ivan.scbcd6.client;
import java.io.IOException;
import java.io.PrintWriter;
import
import
import
import
import
javax.servlet.ServletException;
javax.servlet.annotation.WebServlet;
javax.servlet.http.HttpServlet;
javax.servlet.http.HttpServletRequest;
javax.servlet.http.HttpServletResponse;
import com.ivan.scbcd6.StatefulSession1Bean;
/**
* Servlet implementing a local EJB client.
*
* @author Ivan A Krizsan
*/
@WebServlet(name = "StatefulSession1Servlet", urlPatterns = "/test.do")
public class StatefulSession1Servlet extends HttpServlet
{
/* Constant(s): */
private static final long serialVersionUID = 1L;
/** Name of HTTP session variable holding reference to stateful session bean. */
public static final String STATEFUL_EJB_HTTP_SESSION_VAR =
"_statefulSession1Bean";
/**
* Processes GET request to the servlet.
*/
@Override
protected void doGet(HttpServletRequest inRequest,
HttpServletResponse inResponse) throws ServletException, IOException
{
StatefulSession1Bean theStatefulSessionBean = null;
theStatefulSessionBean = (StatefulSession1Bean)inRequest.
getSession().getAttribute(STATEFUL_EJB_HTTP_SESSION_VAR);
PrintWriter theResponseWriter = inResponse.getWriter();
if (theStatefulSessionBean != null) {
/* Ask the stateful session bean to extend a greeting. */
String theRequestNameParam = inRequest.getParameter("name");
if (theRequestNameParam == null)
{
theRequestNameParam = "Anonymous Coward";
23
}
String theResponse = theStatefulSessionBean.greeting(theRequestNameParam);
theResponseWriter.println("Response from the EJB: " + theResponse);
theStatefulSessionBean.remove();
} else {
theResponseWriter.println(
"Unable to retrieve an instance of the stateful session bean.");
}
}
}
package com.ivan.scbcd6.client;
import
import
import
import
javax.naming.InitialContext;
javax.naming.NamingException;
javax.servlet.http.HttpSessionEvent;
javax.servlet.http.HttpSessionListener;
/**
* HTTP session listener that, for each client session, retrieves
* a reference to a stateful session bean and stores it in the HTTP
* session, in order for a servlet to use one and the same stateful
* session bean for all the invocations made by one single client.
*
* @author Ivan A Krizsan
*/
public class StatefulSessionBeanSessionListener implements HttpSessionListener
{
/**
* Performs additional initialization of a newly created HTTP session.
*/
@Override
public void sessionCreated(final HttpSessionEvent inHttpSessionEvent)
{
System.out.println("*** HTTP session created.");
try
{
/*
* Retrieve a reference to the stateful session bean.
* Note that the reference is not cast to the type of the
* session bean, since no invocations are made on the bean.
*
* Use the JNDI API to look up a reference to the session bean
* the servlet is a client of.
* The following JNDI names can be used:
* java:global/StatefulSession1Web/StatefulSession1Bean
* java:global/StatefulSession1Web/StatefulSession1Bean!
com.ivan.scbcd6.StatefulSession1Bean
* java:app/StatefulSession1Web/StatefulSession1Bean
* java:app/StatefulSession1Web/StatefulSession1Bean!
com.ivan.scbcd6.StatefulSession1Bean
* java:module/StatefulSession1Bean
* java:module/StatefulSession1Bean!com.ivan.scbcd6.StatefulSession1Bean
*/
InitialContext theInitialContext = new InitialContext();
Object theBeanReference =
theInitialContext.lookup("java:module/StatefulSession1Bean");
if (theBeanReference != null) {
/* Store the reference in the HTTP session. */
inHttpSessionEvent.getSession().setAttribute(
StatefulSession1Servlet.STATEFUL_EJB_HTTP_SESSION_VAR,
theBeanReference);
System.out.println(
"*** Stateful session bean reference stored in HTTP session.");
} else {
System.out.println(
"*** Unable to find reference to stateful session bean.");
24
}
} catch (final NamingException theException)
{
theException.printStackTrace();
}
}
/* (non-Javadoc)
* @see
javax.servlet.http.HttpSessionListener#sessionDestroyed(javax.servlet.http.HttpSessionEv
ent)
*/
@Override
public void sessionDestroyed(final HttpSessionEvent inHttpSessionEvent)
{
/*
* No need to release the reference to the stateful session bean
* when the HTTP session is destroyed, as this will be taken care
* of by the container.
*/
System.out.println("*** HTTP session destroyed.");
}
}
Within 10 seconds, issue another request in the browser. For instance to the following URL:
http://localhost:8080/StatefulSession1Web/test.do?name=Steven
The URL may differ if you have chosen a different project name.
For each request you should see a greeting string from the session bean in the browser.
In the console, there should be one single log entry saying that one instance of the stateful
session bean was created.
Regardless of how many times the request to the URL is sent from the browser, there will
25
In the StatefulSession1Servlet class, modify the doGet method to look like this:
...
protected void doGet(HttpServletRequest inRequest,
HttpServletResponse inResponse) throws ServletException, IOException
{
PrintWriter theResponseWriter = inResponse.getWriter();
String theRequestNameParam = inRequest.getParameter("name");
if (theRequestNameParam == null)
{
theRequestNameParam = "Anonymous Coward";
}
String theResponse = mStatefulSessionBean.greeting(theRequestNameParam);
theResponseWriter.println("Response from the EJB: " + theResponse);
mStatefulSessionBean.remove();
}
...
Any subsequent requests, despite being made within 10 seconds of the prior request, will
result in a NoSuchEJBException being thrown.
Note that:
The session bean implementation class is annotated with the @LocalBean annotation.
This annotation is used to annotate session beans that expose a no-interface view.
If the session bean does not expose any other view, then the @LocalBean annotation is
optional and the session bean will expose a no-interface view.
The session bean implementation class is annotated with the @Stateful annotation.
This annotation is used to annotate session beans that are to be stateful.
For details about the options available with the @Stateful annotation, please see the section
on Session Bean Metadata in the chapter on session beans.
The session bean implementation class is annotated with the @StatefulTimeout annotation.
This annotation is used to tell the container the idle time after which an instance of the
stateful session bean will be eligible for removal.
When waiting too long, 10 seconds in the case of this example, the EJB instance is removed
by the container. Any subsequent attempts to invoke the EJB instance will cause a
26
NoSuchEJBException to be thrown.
As before, when a session bean has a no-interface view, the business methods are all the
public methods in the bean implementation class.
The remove method in the session bean implementation class is annotated with the
@Remove annotation.
When the servlet calls the remove method on an instance of the stateful session bean, it
signals that it is done with the stateful session bean and the container disposes the EJB
instance, as seen in the console log.
The reason why we cannot have an instance field of the type StatefulSession1Bean in the
servlet class and annotate it with @EJB and have the container inject an instance of the
stateful session bean is that the server class is not dedicated to any particular client, but may
serve requests from different clients.
In such a case, one and the same instance of the stateful session bean will be called for all
the clients.
27
The session bean implementation class is not annotated with the @LocalBean annotation.
The EJB is developed in a web project, since the session bean will only have a local view, albeit
with a local business interface, and can thus only be accessed by clients in the same application.
In the new project, create the session bean implementation class as below:
package com.ivan.scbcd6;
import java.util.Date;
import javax.annotation.PostConstruct;
import javax.ejb.Stateful;
/**
* Simplest possible stateful session bean exposing a local business interface view.
*/
@Stateful
public class StatefulSession2Bean implements StatefulSession2Local
{
private static int sCurrentInstanceNumber = 1;
private int mInstanceNumber;
@PostConstruct
public void initialize()
{
mInstanceNumber = sCurrentInstanceNumber++;
System.out.println("*** StatefulSession2Bean " + mInstanceNumber +
" created.");
}
/* (non-Javadoc)
* @see com.ivan.scbcd6.StatefulSession2Local#greeting(java.lang.String)
*/
@Override
public String greeting(final String inName)
{
Date theCurrentTime = new Date();
String theMessage = "Hello " + inName + ", I am stateful session bean " +
mInstanceNumber + ". The time is now: " + theCurrentTime;
return theMessage;
}
}
package com.ivan.scbcd6;
import javax.ejb.Local;
@Local
public interface StatefulSession2Local
{
/**
* Creates a greeting to the person with the supplied name.
28
*
* @param inName Name of person to greet.
* @return Greeting.
*/
public String greeting(final String inName);
}
Note that:
The session bean implementation class is annotated with the @Stateful annotation.
The local business interface does not extend any other interface.
With EJB 2.1 and earlier, the local business interface had to extend the
javax.ejb.EJBLocalObject. This is no longer necessary.
29
The interface implemented by the session bean implementation class is annotated with the
@Remote annotation.
The session bean with a remote view can be created in an EJB project, since it may be accessed by
clients outside of the application in which it is deployed.
In the new project, create the session bean implementation class as below:
package com.ivan.scbcd6;
import java.util.Date;
import javax.ejb.Stateful;
/**
* Simplest possible stateful session bean with a remote business interface view.
*/
@Stateful
public class StatefulSession3Bean implements StatefulSession3Remote
{
private static int sCurrentInstanceNumber = 1;
private int mInstanceNumber;
@PostConstruct
public void initialize()
{
mInstanceNumber = sCurrentInstanceNumber++;
System.out.println("*** StatefulSession3Bean " + mInstanceNumber +
" created.");
}
/* (non-Javadoc)
* @see com.ivan.scbcd6.StatefulSession3Remote#greeting(java.lang.String)
*/
@Override
public String greeting(final String inName)
{
Date theCurrentTime = new Date();
String theMessage = "Hello " + inName + ", I am stateful session bean " +
mInstanceNumber + ". The time is now: " + theCurrentTime;
return theMessage;
}
}
package com.ivan.scbcd6;
import javax.ejb.Remote;
/**
* Remote business interface of the stateful session bean.
*/
@Remote
public interface StatefulSession3Remote
{
/**
* Creates a greeting to the person with the supplied name.
*
* @param inName Name of person to greet.
* @return Greeting.
30
*/
public String greeting(final String inName);
}
Remote clients accessing the stateful session bean with the remote business interface will be
developed in the second section of the chapter on session bean clients.
Note that:
The session bean implementation class is annotated with the @Stateful annotation.
The remote business interface does not extend any other interface.
With EJB 2.1 and earlier, the remote business interface had to extend the
javax.ejb.EJBObject. This is no longer necessary.
31
In the package com.ivan.scbcd6, create the session bean implementation class implemented
as below:
package com.ivan.scbcd6;
import java.util.Date;
/**
* Simplest possible stateful session bean without using annotations.
*/
public class StatefulSession4Bean
{
private static int sCurrentInstanceNumber = 1;
private int mInstanceNumber;
@PostConstruct
public void initialize()
{
mInstanceNumber = sCurrentInstanceNumber++;
System.out.println("*** StatefulSession4Bean " + mInstanceNumber +
" created.");
}
/**
* Creates a greeting to the person with the supplied name.
*
* @param inName Name of person to greet.
* @return Greeting.
*/
public String greeting(final String inName)
{
Date theCurrentTime = new Date();
String theMessage = "Hello " + inName + ", I am stateful session bean " +
mInstanceNumber + ". The time is now: " + theCurrentTime;
return theMessage;
}
}
In the WebContent/WEB-INF directory, create a file named ejb-jar.xml with the following
contents:
32
<session>
<ejb-name>StatefulSession4Bean</ejb-name>
<!-Indicates the session bean has a no-interface view.
Equivalent to the @LocalBean annotation.
-->
<local-bean/>
<!-- Specify the session bean implementation class. -->
<ejb-class>com.ivan.scbcd6.StatefulSession4Bean</ejb-class>
<!-Specifies that the session bean is stateful.
Equivalent to the @Stateful annotation.
-->
<session-type>Stateful</session-type>
</session>
</enterprise-beans>
</ejb-jar>
Create a client servlet in the same way as in the first section on creating stateful session
beans but with the type of the instance variable in which the EJB reference is to be injected
being StatefulSession4Bean.
Deploy the project to the GlassFish application server.
In a browser, issue a request to the following URL:
http://localhost:8080/StatefulSession4Web/test.do?name=Ivan
The URL may differ if you have chosen a different project name.
Again, you will see the response from the session bean in the browser.
Note that:
There are no special interfaces to implement in the session bean implementation class.
There are no special superclass that the session bean implementation class inherits from.
The <session-type> element with the value Stateful in the deployment descriptor is equal
to the @Stateful annotation we have seen in earlier examples.
This element is used to specify the type of the session bean stateful in this case.
33
34
Constructor
Allowed Operations
None.
SessionContext:
getBusinessObject,
getEJBHome,
getEJBLocalHome,
getCallerPrincipal,
isCallerInRole,
getEJBObject,
getEJBLocalObject,
lookup,
getContextData,
getUserTransaction (BMT only).
JNDI Access: Available
Resource managers: Accessible.
EntityManagerFactory: Accessible.
SessionContext:
getBusinessObject,
getEJBHome,
getEJBLocalHome,
getCallerPrincipal,
isCallerInRole,
getEJBObject,
getEJBLocalObject,
lookup,
getContextData,
35
getInvokedBusinessInterface,
wasCancelCalled,
getUserTransaction (BMT only),
getRollbackOnly (CMT only),
setRollbackOnly (CMT only).
JNDI Access: Available
Resource managers: Accessible.
Other EJBs: Accessible.
EntityManagerFactory: Accessible.
EntityManager: Accessible.
Timer methods: Accessible.
UserTransaction methods: Accessible (BMT
only).
afterBegin and beforeCompletion
(when EJB implements the
SessionSynchronization interface) or methods in
the EJB annotated with @AfterBegin and
@BeforeCompletion
In addition, the getRollbackOnly and setRollbackOnly methods of the SessionContext interface may
only be invoked from within a method executing in a transaction context or else an
IllegalStateException will be thrown.
36
In the package com.ivan.scbcd6, create the session bean implementation class implemented
as below:
package com.ivan.scbcd6;
import
import
import
import
java.util.Date;
javax.annotation.PostConstruct;
javax.ejb.LocalBean;
javax.ejb.Stateless;
/**
* Simplest possible stateless session bean exposing a local, no-interface view.
*/
@Stateless
@LocalBean
public class StatelessSession1Bean
{
private static int sCurrentInstanceNumber = 1;
private int mInstanceNumber;
@PostConstruct
public void initialize()
{
mInstanceNumber = sCurrentInstanceNumber++;
System.out.println("*** StatelessSession1Bean " + mInstanceNumber +
" created.");
}
/**
* Creates a greeting to the person with the supplied name.
*
* @param inName Name of person to greet.
* @return Greeting.
*/
public String greeting(final String inName)
{
Date theCurrentTime = new Date();
String theMessage = "Hello " + inName + ", I am stateless session bean " +
mInstanceNumber + ". The time is now: " + theCurrentTime;
return theMessage;
}
}
37
Create the package com.ivan.scbcd6.client and in it, create the client servlet implemented as
follows:
package com.ivan.scbcd6.client;
import java.io.IOException;
import java.io.PrintWriter;
import
import
import
import
import
import
javax.ejb.EJB;
javax.servlet.ServletException;
javax.servlet.annotation.WebServlet;
javax.servlet.http.HttpServlet;
javax.servlet.http.HttpServletRequest;
javax.servlet.http.HttpServletResponse;
import com.ivan.scbcd6.StatelessSession1Bean;
/**
* Servlet implementing a local EJB client.
*
* @author Ivan A Krizsan
*/
@WebServlet(name = "StatelessSession1Servlet", urlPatterns = "/test.do")
public class StatelessSession1Servlet extends HttpServlet
{
/* Constant(s): */
private static final long serialVersionUID = 1L;
/* Instance variable(s): */
@EJB
private StatelessSession1Bean mStatelessSessionBean;
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
@Override
protected void doGet(HttpServletRequest inRequest,
HttpServletResponse inResponse) throws ServletException, IOException
{
PrintWriter theResponseWriter = inResponse.getWriter();
String theRequestNameParam = inRequest.getParameter("name");
if (theRequestNameParam == null)
{
theRequestNameParam = "Anonymous Coward";
}
String theResponse = mStatelessSessionBean.greeting(theRequestNameParam);
theResponseWriter.println("Response from the EJB: " + theResponse);
}
}
38
Note that:
The session bean implementation class is annotated with the @LocalBean annotation.
This annotation is used to annotate session beans that expose a no-interface view.
If the session bean does not expose any other view, then the @LocalBean annotation is
optional and the session bean will expose a no-interface view.
The session bean implementation class is annotated with the @Stateless annotation.
This annotation is used to annotate session beans that are to be stateless.
For details about the options available with the @Stateless annotation, please see the section
on Session Bean Metadata in the chapter on session beans.
As before, when a session bean has a no-interface view, the business methods are all the
public methods in the bean implementation class.
39
The session bean implementation class is not annotated with the @LocalBean annotation.
The EJB is developed in a web project, since the session bean will only have a local view, albeit
with a local business interface, and can thus only be accessed by clients in the same application.
In the new project, create the session bean implementation class as below:
package com.ivan.scbcd6;
import java.util.Date;
import javax.annotation.PostConstruct;
import javax.ejb.Stateless;
/**
* Simplest possible stateless session bean exposing a local business interface view.
*/
@Stateless
public class StatelessSession2Bean implements StatelessSession2Local
{
private static int sCurrentInstanceNumber = 1;
private int mInstanceNumber;
@PostConstruct
public void initialize()
{
mInstanceNumber = sCurrentInstanceNumber++;
System.out.println("*** StatelessSession2Bean " + mInstanceNumber +
" created.");
}
/* (non-Javadoc)
* @see com.ivan.scbcd6.StatelessSession2Local#greeting(java.lang.String)
*/
@Override
public String greeting(final String inName)
{
Date theCurrentTime = new Date();
String theMessage = "Hello " + inName + ", I am stateless session bean " +
mInstanceNumber + ". The time is now: " + theCurrentTime;
return theMessage;
}
}
40
package com.ivan.scbcd6;
import javax.ejb.Local;
@Local
public interface StatelessSession2Local
{
/**
* Creates a greeting to the person with the supplied name.
*
* @param inName Name of person to greet.
* @return Greeting.
*/
public String greeting(final String inName);
}
Note that:
The session bean implementation class is annotated with the @Stateless annotation.
The local business interface does not extend any other interface.
With EJB 2.1 and earlier, the local business interface had to extend the
javax.ejb.EJBLocalObject. This is no longer necessary.
41
The interface implemented by the session bean implementation class is annotated with the
@Remote annotation.
The session bean with a remote view can be created in an EJB project, since it may be accessed by
clients outside of the application in which it is deployed.
In the new project, create the session bean implementation class as below:
package com.ivan.scbcd6;
import java.util.Date;
import javax.ejb.Stateless;
/**
* Simplest possible stateless session bean with a remote business interface view.
*/
@Stateless
public class StatelessSession3Bean implements StatelessSession3Remote
{
private static int sCurrentInstanceNumber = 1;
private int mInstanceNumber;
@PostConstruct
public void initialize()
{
mInstanceNumber = sCurrentInstanceNumber++;
System.out.println("*** StatelessSession3Bean " + mInstanceNumber +
" created.");
}
/* (non-Javadoc)
* @see com.ivan.scbcd6.StatelessSession3Remote#greeting(java.lang.String)
*/
@Override
public String greeting(final String inName)
{
Date theCurrentTime = new Date();
String theMessage = "Hello " + inName + ", I am stateless session bean " +
mInstanceNumber + ". The time is now: " + theCurrentTime;
return theMessage;
}
}
package com.ivan.scbcd6;
import javax.ejb.Remote;
/**
* Remote business interface of the stateless session bean.
*/
@Remote
public interface StatelessSession3Remote
{
/**
* Creates a greeting to the person with the supplied name.
*
* @param inName Name of person to greet.
* @return Greeting.
42
*/
public String greeting(final String inName);
}
Remote clients accessing the stateful session bean with the remote business interface will be
developed in chapter five, section 2.
Note that:
The session bean implementation class is annotated with the @Stateless annotation.
The remote business interface does not extend any other interface.
With EJB 2.1 and earlier, the remote business interface had to extend the
javax.ejb.EJBObject. This is no longer necessary.
43
In the package com.ivan.scbcd6, create the session bean implementation class implemented
as below:
package com.ivan.scbcd6;
import java.util.Date;
/**
* Simplest possible stateless session bean without using annotations.
*/
public class StatelessSession4Bean
{
private static int sCurrentInstanceNumber = 1;
private int mInstanceNumber;
@PostConstruct
public void initialize()
{
mInstanceNumber = sCurrentInstanceNumber++;
System.out.println("*** StatelessSession4Bean " + mInstanceNumber +
" created.");
}
/**
* Creates a greeting to the person with the supplied name.
*
* @param inName Name of person to greet.
* @return Greeting.
*/
public String greeting(final String inName)
{
Date theCurrentTime = new Date();
String theMessage = "Hello " + inName + ", I am stateless session bean " +
mInstanceNumber + ". The time is now: " + theCurrentTime;
return theMessage;
}
}
In the WebContent/WEB-INF directory, create a file named ejb-jar.xml with the following
contents:
44
<session>
<ejb-name>StatefulSession1Bean</ejb-name>
<!-Indicates the session bean has a no-interface view.
Equivalent to the @LocalBean annotation.
-->
<local-bean/>
<!-- Specify the session bean implementation class. -->
<ejb-class>com.ivan.scbcd6.StatelessSession4Bean</ejb-class>
<!-Specifies that the session bean is stateless.
Equivalent to the @Stateless annotation.
-->
<session-type>Stateless</session-type>
</session>
</enterprise-beans>
</ejb-jar>
Create a client servlet in the same way as in the first section on creating stateless session
beans.
Note that:
There are no special interfaces to implement in the session bean implementation class.
There are no special superclass that the session bean implementation class inherits from.
The <session-type> element with the value Stateless in the deployment descriptor is equal
to the @Stateless annotation we have seen in earlier examples.
This element is used to specify the type of the session bean stateless in this case.
45
46
Constructor
Allowed Operations
None.
SessionContext:
getBusinessObject,
getEJBHome,
getEJBLocalHome,
getEJBObject,
getEJBLocalObject,
lookup,
getContextData,
getTimerService,
getUserTransaction (BMT only).
JNDI Access: Available
EntityManagerFactory: Accessible.
SessionContext:
getBusinessObject,
getEJBHome,
getEJBLocalHome,
getCallerPrincipal,
isCallerInRole,
getEJBObject,
getEJBLocalObject,
lookup,
getContextData,
getInvokedBusinessInterface,
wasCancelCalled,
getTimerService,
47
SessionContext:
getBusinessObject,
getEJBHome,
getEJBLocalHome,
getCallerPrincipal,
isCallerInRole,
getEJBObject,
getEJBLocalObject,
lookup,
getContextData,
getTimerService,
getMessageContext,
getUserTransaction (BMT only),
getRollbackOnly (CMT only),
setRollbackOnly (CMT only).
MessageContext methods: Available
JNDI Access: Available
Resource managers: Accessible.
Other EJBs: Accessible.
EntityManagerFactory: Accessible.
EntityManager: Accessible.
Timer and TimerService methods: Accessible.
UserTransaction methods: Accessible (BMT
only).
SessionContext:
getBusinessObject,
getEJBHome,
getEJBLocalHome,
getCallerPrincipal,
isCallerInRole,
getEJBObject,
getEJBLocalObject,
lookup,
getContextData,
getTimerService,
getUserTransaction (BMT only),
getRollbackOnly (CMT only),
setRollbackOnly (CMT only).
48
49
Can be shared.
An instance of a singleton session bean is not destroyed until the application shuts down.
The instance even survives system exceptions from business methods and callback methods
invoked after creation and initialization of the bean has completed.
As with stateless and stateful session beans, singleton session beans can also have three different
kinds of views; local no-interface view, local business interface view and a remote business
interface view. Examples on how to configure the different kinds of views has been shown in
chapters two and three and will not be repeated here.
50
In the package com.ivan.scbcd6, create the first session bean implementation class according
to the following listing:
package com.ivan.scbcd6;
import
import
import
import
java.util.Date;
javax.annotation.PostConstruct;
javax.ejb.LocalBean;
javax.ejb.Singleton;
/**
* This class implements the first singleton session bean of this
* example program.
* This singleton session bean has the ability to hold state.
*/
@Singleton
@LocalBean
public class SingletonSessionBeanA
{
/* Constant(s): */
private final static String BEAN_NAME = "SingletonSessionBeanA";
/* Instance variable(s): */
private String mStoredMessage = "[no message set]";
@PostConstruct
public void intialize()
{
System.out.println("*** " + BEAN_NAME + " - Initialized");
}
@PreDestroy
public void cleanUp()
{
System.out.println("*** " + BEAN_NAME + " - Destroyed");
}
public String retrieveMessage()
{
Date theCurrentTime = new Date();
return "Message from " + BEAN_NAME + " - " + mStoredMessage + " "
+ theCurrentTime;
}
public void storeMessage(final String inStoredMessage)
{
mStoredMessage = inStoredMessage;
}
}
51
In the same package, create the second singleton session bean implementation class:
package com.ivan.scbcd6;
import
import
import
import
java.util.Date;
javax.annotation.PostConstruct;
javax.ejb.LocalBean;
javax.ejb.Singleton;
/**
* This class implements the second singleton session bean of this
* example program.
*/
@Singleton
@LocalBean
public class SingletonSessionBeanB
{
private final static String BEAN_NAME = "SingletonSessionBeanB";
@PostConstruct
public void intialize()
{
System.out.println("*** " + BEAN_NAME + " - Initialized");
}
@PreDestroy
public void cleanUp()
{
System.out.println("*** " + BEAN_NAME + " - Destroyed");
}
public String retrieveMessage()
{
Date theCurrentTime = new Date();
return "Message from " + BEAN_NAME + " - " + theCurrentTime;
}
}
In the com.ivan.scbcd6.client package, create the servlet that is to be the client of the two
singleton session beans we just implemented:
package com.ivan.scbcd6.client;
import
import
import
import
import
import
import
import
java.io.IOException;
java.io.PrintWriter;
javax.ejb.EJB;
javax.servlet.ServletException;
javax.servlet.annotation.WebServlet;
javax.servlet.http.HttpServlet;
javax.servlet.http.HttpServletRequest;
javax.servlet.http.HttpServletResponse;
import com.ivan.scbcd6.SingletonSessionBeanA;
import com.ivan.scbcd6.SingletonSessionBeanB;
/**
* Servlet acting as a client of the two singleton session beans.
*/
@WebServlet(name = "SingletonClientServlet", urlPatterns = "/test.do")
public class SingletonClientServlet extends HttpServlet
{
/* Constant(s): */
private static final long serialVersionUID = 1L;
private final static String STORE_ACTION = "store";
private final static String CLEAR_ACTION = "clear";
/* Instance variable(s): */
@EJB
private SingletonSessionBeanA mSingletonBeanA;
@EJB
private SingletonSessionBeanB mSingletonBeanB;
52
@Override
protected void doGet(HttpServletRequest inRequest,
HttpServletResponse inResponse) throws ServletException, IOException
{
System.out.println("**** Entering SingletonClientServlet");
String theRequestNameParam = inRequest.getParameter("name");
String theRequestActionParam = inRequest.getParameter("action");
/* Set default name if none provided. */
if (theRequestNameParam == null || theRequestNameParam.equals(""))
{
theRequestNameParam = "Anonymous Coward";
}
/* Generate output from servlet using session beans. */
PrintWriter theResponseWriter = inResponse.getWriter();
String theMessage;
theMessage = mSingletonBeanA.retrieveMessage();
theResponseWriter.println(theMessage);
theMessage = mSingletonBeanB.retrieveMessage();
theResponseWriter.println(theMessage);
/*
* Store or clear data in one of the singleton session beans
* if the supplied action so specifies.
*/
if (theRequestActionParam != null)
{
if (STORE_ACTION.equals(theRequestActionParam))
{
mSingletonBeanA.storeMessage(theRequestNameParam);
}
if (CLEAR_ACTION.equals(theRequestActionParam))
{
mSingletonBeanA.storeMessage("[CLEARED]");
}
}
System.out.println("**** Exiting SingletonClientServlet");
theResponseWriter.println("Finished invoking singleton session beans!");
}
}
Message from SingletonSessionBeanA - [no message set] Mon Aug 23 07:05:27 CEST 2010
Message from SingletonSessionBeanB - Mon Aug 23 07:05:27 CEST 2010
Finished invoking singleton session beans!
53
Any subsequent requests to the last URL will cause the same name to be retrieved.
Note that:
Both the singleton session bean implementation classes are annotated using the @Singleton
annotation.
The singleton session beans are also annotated with the @LocalBean annotation.
We recognize this annotation from earlier examples as declaring that the bean has a local,
no-interface view.
As before, when a session bean has a no-interface view, the business methods are all the
public methods in the bean implementation class.
Both the singleton session beans have initialization methods annotated with the
@PostConstruct annotation.
Such methods are invoked after construction of an EJB instance, immediately prior to the
instance being taken into use.
Both the singleton session beans also have methods annotated with the @PreDestroy
annotation.
These methods are invoked immediately prior to an instance being taken out of service.
54
In the example program, modify the SingletonSessionBeanA class adding the @Startup
annotation:
...
@Singleton
@LocalBean
@Startup
public class SingletonSessionBeanA
{
...
To see what happens when our application is shut down and restarted, navigate to the
Applications node in the GlassFish v3 administration console and click Restart in the Action
column for our application. A message in the administration console should confirm that the
application has been restarted:
Having restarted the application, you should see the following output in the GlassFish log
file or in the console:
55
Note that:
If the application has not been run, then you will only see a message about
SingletonSessionBeanA having been destroyed, not SingletonSessionBeanB, since the latter
has not been created.
56
In the example program, modify the SingletonSessionBeanA class adding the @DependsOn
annotation:
...
@Singleton
@LocalBean
@Startup
@DependsOn("SingletonSessionBeanB")
public class SingletonSessionBeanA
{
...
As after having added the @Startup annotation, navigate to the Applications node in the
GlassFish v3 administration console and click Restart in the Action column for our
application.
Having restarted the application, you should see the following output in the GlassFish log
file or in the console.
Note that there are now messages from both the singleton session beans saying that they
have been initialized and that SingletonSessionBeanB is initialized before
SingletonSessionBeanA is initialized this is what we wanted to accomplish using the
@DependsOn annotation.
If we restart the application again from the GlassFish administration console, we can also
note that SingletonSessionBeanA is destroyed prior to SingletonSessionBeanB.
This is also caused by SingletonSessionBeanA having a dependency on
SingletonSessionBeanB. The container ensures that all singleton session beans which a
singleton session beans depend on are still present during destruction.
57
REQUIRED
REQUIRES_NEW
NOT_SUPPORTED
See the section on transaction attributes in the chapter on transactions for details on transaction
attributes.
58
In the package com.ivan.scbcd6, create the first session bean implementation class according
to the following listing:
package com.ivan.scbcd6;
import
import
import
import
javax.ejb.ConcurrencyManagement;
javax.ejb.ConcurrencyManagementType;
javax.ejb.LocalBean;
javax.ejb.Singleton;
/**
* This class implements a singleton session bean with container-managed
* concurrency.
*/
@Singleton
@LocalBean
@ConcurrencyManagement(ConcurrencyManagementType.CONTAINER)
public class SingletonSessionBeanA
{
public void slowMethod()
{
System.out.println("SingletonSessionBeanA - Entering slowMethod");
waitSomeTime(10);
System.out.println("SingletonSessionBeanA - Exiting slowMethod");
}
59
In the same package, create the second singleton session bean implementation class:
package com.ivan.scbcd6;
import
import
import
import
javax.ejb.ConcurrencyManagement;
javax.ejb.ConcurrencyManagementType;
javax.ejb.LocalBean;
javax.ejb.Singleton;
/**
* This class implements a singleton session bean with bean-managed
* concurrency.
*/
@Singleton
@LocalBean
@ConcurrencyManagement(ConcurrencyManagementType.BEAN)
public class SingletonSessionBeanB
{
public void slowMethod()
{
System.out.println("SingletonSessionBeanB - Entering slowMethod");
waitSomeTime(10);
System.out.println("SingletonSessionBeanB - Exiting slowMethod");
}
public void fastMethod()
{
System.out.println("SingletonSessionBeanB - Entering fastMethod");
waitSomeTime(1);
System.out.println("SingletonSessionBeanB - Exiting fastMethod");
}
private void waitSomeTime(final long inSecondsDelay)
{
try
{
Thread.sleep(1000L * inSecondsDelay);
} catch (InterruptedException e)
{
// Ignore exceptions.
}
}
}
60
In the com.ivan.scbcd6.client package, create the servlet that is to be the client of the two
singleton session beans we just implemented:
package com.ivan.scbcd6.client;
import java.io.IOException;
import java.io.PrintWriter;
import
import
import
import
import
import
javax.ejb.EJB;
javax.servlet.ServletException;
javax.servlet.annotation.WebServlet;
javax.servlet.http.HttpServlet;
javax.servlet.http.HttpServletRequest;
javax.servlet.http.HttpServletResponse;
import com.ivan.scbcd6.SingletonSessionBeanA;
import com.ivan.scbcd6.SingletonSessionBeanB;
/**
* Servlet acting as a client of the two singleton session beans.
*/
@WebServlet(name = "SingletonClientServlet", urlPatterns = "/test.do")
public class SingletonClientServlet extends HttpServlet
{
/* Constant(s): */
private static final long serialVersionUID = 1L;
/* Instance variable(s): */
@EJB
private SingletonSessionBeanA mSingletonBeanA;
@EJB
private SingletonSessionBeanB mSingletonBeanB;
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
@Override
protected void doGet(HttpServletRequest inRequest,
HttpServletResponse inResponse) throws ServletException, IOException
{
System.out.println("*** Entering SingletonClientServlet");
/* Get parameter specifying which test to run. */
String theSelectorString = inRequest.getParameter("selector");
if (theSelectorString == null || theSelectorString.equals(""))
{
theSelectorString = "1";
}
int theSelector = Integer.parseInt(theSelectorString);
switch (theSelector)
{
case 1: testContainerManagedConcurrency(); break;
case 2: testBeanManagedConcurrency(); break;
default: break;
}
System.out.println("*** Exiting SingletonClientServlet");
/* Display a message on the web page. */
PrintWriter theResponseWriter = inResponse.getWriter();
theResponseWriter.println("Finished invoking singleton session bean " +
"concurrency test " + theSelector);
}
private void testContainerManagedConcurrency()
{
System.out.println("*** Entering testContainerManagedConcurrency");
/*
* Call first the slow method and then the fast method from separate
* threads.
*/
System.out.println("
Calling slowMethod...");
61
new Thread()
{
@Override
public void run()
{
mSingletonBeanA.slowMethod();
}
}.start();
System.out.println("
Calling fastMethod...");
new Thread()
{
@Override
public void run()
{
mSingletonBeanA.fastMethod();
}
}.start();
System.out.println("*** Exiting testContainerManagedConcurrency");
}
private void testBeanManagedConcurrency()
{
System.out.println("*** Entering testBeanManagedConcurrency");
/*
* Call first the slow method and then the fast method from separate
* threads.
*/
System.out.println("
Calling slowMethod...");
new Thread()
{
@Override
public void run()
{
mSingletonBeanB.slowMethod();
}
}.start();
System.out.println("
Calling fastMethod...");
new Thread()
{
@Override
public void run()
{
mSingletonBeanB.fastMethod();
}
}.start();
System.out.println("*** Exiting testBeanManagedConcurrency");
}
}
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
62
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
Note that:
63
4.6.2.Concurrency Management
As we have seen, there are two different types of concurrency management for singleton session
beans:
Container-managed concurrency.
Bean-managed concurrency.
The type of concurrency management is configured using either an annotation or in the ejb-jar.xml
deployment descriptor.
The @ConcurrencyManagement Annotation
The concurrency management mode of a singleton session bean can be configured using the
@ConcurrencyManagement annotation or the <concurrency-management-type> element in the ejbjar.xml deployment descriptor.
The @ConcurrencyManagement annotation has one single element:
Element
Description
value
While container-managed concurrency is, as the name hints, container-managed the developer still
has some opportunities to influence the concurrent behaviour of the singleton session bean, as we
will see in subsequent sections.
With bean-managed concurrency, the container allows full concurrent access and it is left up to the
bean developer to impose any restrictions, as will be discussed further in the section on beanmanaged concurrency.
Container-Managed Concurrency
Container-managed concurrency means that the container controls the concurrent access to bean
instances. The bean developer is able to further configure container-managed concurrency using
the @Lock annotation.
64
The bean developer can control the concurrent access by applying the @Lock annotation to either
the class or to individual methods in the bean using one of two values:
LockType.READ
Any number of threads may simultaneously execute methods with read-locks.
LockType.WRITE
Default value.
One single thread may execute in a method with write-lock. No other threads may execute in
the same instance.
A class-level @Lock annotation in the superclass applies to the (inherited) methods defined
in the superclass.
A method-level @Lock annotation in the superclass applies to the (inherited) method which
it annotates.
A method defined in a subclass does not inherit the @Lock annotation, neither that from the
superclass nor from a method it overrides.
Threads that are not allowed to execute a method due to a lock being held are blocked until the lock
is released, except when a timeout has been specified using the @AccessTimeout annotation.
The @AccessTimeout annotation can be specified at class-level, method-level or both. In the latter
case, the method-level annotation takes precedence. The @AccessTimeout annotation has the
following elements:
Element
Description
value
unit
65
To see how a change of concurrency locking attribute affects our example program introduced in
the previous section, we make one single modification to the singleton session bean with container
managed concurrency:
...
@Singleton
@LocalBean
@ConcurrencyManagement(ConcurrencyManagementType.CONTAINER)
@Lock(LockType.READ)
public class SingletonSessionBeanA
{
...
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
Note that:
To see an example on how to add timeout to a business method in a singleton session bean, we
modify the SingletonSessionBeanA class again:
...
@Singleton
@LocalBean
@ConcurrencyManagement(ConcurrencyManagementType.CONTAINER)
@Lock(LockType.WRITE)
public class SingletonSessionBeanA
...
Annotate the fastMethod with the @AccessTimeout annotation to have a timeout of 100
milliseconds:
...
@AccessTimeout(100)
public void fastMethod()
...
66
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
Note that:
The fastMethod never executes.
The thread wanting to invoke the method times out waiting to access the method and a
ConcurrentAccessTimeoutException is thrown.
The slowMethod executes normally.
Reentrant Locking
The following semantics applies to methods in singleton session beans being called by another
method in the singleton session bean:
If the calling method holds a WRITE lock on the same thread and calls a method has the
lock type READ, then the call will proceed immediately, without releasing the original
WRITE lock.
If the calling method holds a WRITE lock on the same thread and calls a method has the
lock type WRITE, then the call will proceed immediately, without releasing the original
WRITE lock.
If the calling method holds only a READ lock on the same thread and calls a method has the
lock type READ, then the call will proceed immediately, without releasing the original
READ lock.
If the calling method holds only a READ lock on the same thread and calls a method has the
lock type WRITE, then the an IllegalLoopbackException will be thrown.
67
Bean-Managed Concurrency
It is also possible for a singleton session bean to instruct the container to relinquish all concurrencymanagement by annotating a singleton session bean with the
@ConcurrencyManagement(ConcurrencyManagementType.BEAN) annotation or using the ejbjar.xml deployment descriptor.
With bean-managed concurrency, it is up to the bean developer to use standard Java mechanisms,
such as the synchronized and volatile keywords as well as the concurrent utilities in the Java API.
As the final example on singleton session bean concurrency, we'll show that by synchronizing the
two business methods in the SingletonSessionBeanB of our previous example, it will display the
same behaviour as the bean with container-managed concurrency.
...
@Singleton
@LocalBean
@ConcurrencyManagement(ConcurrencyManagementType.BEAN)
public class SingletonSessionBeanB
{
public synchronized void slowMethod()
{
System.out.println("SingletonSessionBeanB - Entering slowMethod");
waitSomeTime(10);
System.out.println("SingletonSessionBeanB - Exiting slowMethod");
}
public synchronized void fastMethod()
{
System.out.println("SingletonSessionBeanB - Entering fastMethod");
waitSomeTime(1);
System.out.println("SingletonSessionBeanB - Exiting fastMethod");
}
...
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
Note that:
The synchronized keyword is used to control concurrent access to the two methods.
68
69
Allowed Operations
Constructor
None.
SessionContext:
lookup.
JNDI Access: Available
SessionContext:
getBusinessObject,
lookup,
getContextData,
getTimerService,
getUserTransaction (BMT only),
getRollbackOnly (CMT only),
setRollbackOnly (CMT only).
JNDI Access: Available
Resource managers: Accessible.
Other EJBs: Accessible.
EntityManagerFactory: Accessible.
EntityManager: Accessible.
Timer service & methods: Accessible.
UserTransaction methods: Accessible (BMT only).
70
EntityManagerFactory: Accessible.
EntityManager: Accessible.
Timer service & methods: Accessible.
UserTransaction methods: Accessible (BMT only).
Business methods from web service endpoint.
SessionContext:
getBusinessObject,
getCallerPrincipal,
isCallerInRole,
lookup,
getContextData,
getTimerService,
getMessageContext,
getUserTransaction (BMT only),
getRollbackOnly (CMT only),
setRollbackOnly (CMT only).
MessageContext methods: Available
JNDI Access: Available
Resource managers: Accessible.
Other EJBs: Accessible.
EntityManagerFactory: Accessible.
EntityManager: Accessible.
Timer service & methods: Accessible.
UserTransaction methods: Accessible (BMT only).
SessionContext:
getBusinessObject,
getCallerPrincipal,
isCallerInRole,
lookup,
getContextData,
getTimerService,
getUserTransaction (BMT only),
getRollbackOnly (CMT only),
setRollbackOnly (CMT only).
JNDI Access: Available
Resource managers: Accessible.
Other EJBs: Accessible.
EntityManagerFactory: Accessible.
EntityManager: Accessible.
Timer service & methods: Accessible.
UserTransaction methods: Accessible (BMT only).
In addition to the constraints specified in the above table, the getRollbackOnly and setRollbackOnly
methods may only be invoked from within a transaction, otherwise an IllegalStateException will be
thrown.
71
Local clients.
Remote clients.
The different types of clients never access session bean instances directly. All access is done
through the session bean's client view.
It is possible for a session bean to have multiple client views but usually only one is supplied.
In this chapter we will look at local and remote clients, but since web service clients of EJBs does
not differ from regular web service clients, there will be no examples of web service clients in this
book.
Other EJBs.
Servlets.
Web services.
A local session bean is accessed in the same manner regardless of the type of client. The difference
is whether the client obtains the reference to the session bean using dependency injection or
whether the reference is looked up using JNDI.
Parameters and return values in a local view of an EJB are passed by reference see the section on
Session Bean Method Parameters for more information.
72
package com.ivan.scbcd6;
import
import
import
import
import
java.util.Date;
java.util.List;
javax.annotation.PostConstruct;
javax.ejb.LocalBean;
javax.ejb.Stateful;
/**
* Simplest possible stateful session bean exposing a local, no-interface view.
*/
@Stateful
@LocalBean
public class StatefulSession1Bean
{
private static int sCurrentInstanceNumber = 1;
private int mInstanceNumber;
@PostConstruct
public void initialize()
{
mInstanceNumber = sCurrentInstanceNumber++;
System.out.println("*** StatefulSession1Bean " + mInstanceNumber +
" created.");
}
/**
* Creates a greeting to the person with the supplied name.
*
* @param inName Name of person to greet.
* @return Greeting.
*/
public String greeting(final String inName)
{
Date theCurrentTime = new Date();
String theMessage = "Hello " + inName + ", I am stateful session bean " +
mInstanceNumber + ". The time is now: " + theCurrentTime;
return theMessage;
}
/**
* Processes the supplied list.
* The purpose of this method is to illustrate the difference
* in parameter passing semantics between local and remote EJBs.
*
* @param inList List to process.
*/
public void processList(List<String> inList)
{
String theListStrings = "";
for (String theString : inList)
{
theListStrings += theString + ", ";
}
System.out.println("\nStatefulSession1Bean.processList: " +
"The list contains: [" + theListStrings + "]");
/*
73
package com.ivan.scbcd6.client;
import
import
import
import
java.io.IOException;
java.io.PrintWriter;
java.util.ArrayList;
java.util.List;
import
import
import
import
import
import
javax.ejb.EJB;
javax.servlet.ServletException;
javax.servlet.annotation.WebServlet;
javax.servlet.http.HttpServlet;
javax.servlet.http.HttpServletRequest;
javax.servlet.http.HttpServletResponse;
import com.ivan.scbcd6.StatefulSession1Bean;
/**
* Servlet implementing a local EJB client.
*/
@WebServlet(name = "LocalEJBClientServlet", urlPatterns = "/test.do")
public class LocalEJBClientServlet extends HttpServlet
{
/* Constant(s): */
private static final long serialVersionUID = 1L;
/* Instance variable(s): */
@EJB
private StatefulSession1Bean mLocalSessionBean;
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
@Override
protected void doGet(HttpServletRequest inRequest,
HttpServletResponse inResponse) throws ServletException, IOException
{
PrintWriter theResponseWriter = inResponse.getWriter();
String theResponse = mLocalSessionBean.greeting("Anonymous");
theResponseWriter.println("Response from the EJB: " + theResponse);
/* Process a list to examine parameter passing semantics. */
List<String> theList = new ArrayList<String>();
theList.add("string 1");
theList.add("string 2");
theList.add("last string");
mLocalSessionBean.processList(theList);
/* Output list after EJB invocation. */
String theListStrings = "";
for (String theString : theList)
{
theListStrings += theString + ", ";
}
System.out.println("\nList after having invoked EJB processList: [" +
theListStrings + "]");
}
}
74
In the Package or Project Explorer in Eclipse, right-click the project and select Run As ->
Run on Server...
When being asked about which server to run the web application on, select the GlassFish v3
server.
Response from the EJB: Hello Anonymous, the time is now: Wed Jul 07 18:42:23 CEST 2010
...
INFO: *** StatefulSession1Bean 1 created.
INFO: StatefulSession1Bean.processList: The list contains: [string 1, string 2, last
string, ]
INFO: List after having invoked EJB processList: [string 1, string 2, last string,
String added in EJB, ]
...
Note that the string added to the list passed as parameter to the processMethod method of the EJB in
the EJB is output by the servlet after the method has finished executing. We can thus confirm that
method parameters are passed by reference to local EJBs.
In the servlet implementation class, note that:
The servlet implementation class is annotated with the @WebServlet annotation, which is
part of the Servlet 3.0 standard. A JavaEE 6 container is thus required when deploying this
web project.
There is an instance field of the type StatefulSession1Bean that is annotated with an @EJB
annotation.
The EJB that the servlet is the client of does not have an interface, thus the servlet has a nointerface view of the EJB. In a case like this, we must use the actual EJB implementation
class when declaring the field holding the reference to the EJB.
In this case, the @EJB annotation annotates an instance field in which an EJB reference is to
be injected. More about injection of EJB references in the section on Dependency Injection
and Resource Lookup in chapter 14.
All the requests from the servlet will be done to one and the same EJB, despite the fact that
the EJB is a stateful session bean which is said to only have one single client.
However, there will be only one single instance of the servlet class serving all requests and
thus only one single client of the EJB. The container will manage the requests to the session
bean as to allow only one single thread at a time to execute in the bean.
An alternative way of performing dependency injection into EJB components using only the ejbjar.xml deployment descriptor. Please refer to the section on dependency injection in the chapter on
session beans for more details.
75
Scope
Comments
java:global[/<app-name>]/<modulename>/<bean-name>[!<fullyqualified-interface-name>]
Global
Global namespace.
The app-name part is only required if the EJB is packed in
an EAR file.
The module of an EJB is the WAR or JAR file it is packaged
in.
The bean-name is the name of the EJB, as can be specified
with the @EJB annotation.
The fully-qualified-interface-name must only be specified if
the session bean has two or more of the interfaces/views
listed below.
java:app/<module-name>/<beanname>[!<fully-qualified-interfacename>]
Application.
Client and
session bean
deployed in the
same
application.
Application-specific namespace.
The module-name part is always required.
java:module/<bean-name>[!<fullyqualified-interface-name>]
Module.
Module-specific namespace.
Client and
session bean
deployed in the
same module of
an application.
In addition, there also is the java:comp JNDI namespace. All components, except for components
contained in web modules, have a java:comp namespace of their own.
All components contained in a web module share one and the same component namespace.
Depending on the different views presented by a session bean, the container registers one JNDI
name for each of the following views:
No-interface view.
The existence of a global JNDI name for a local business interface or a no-interface view does not
mean that access to those entries are allowed from other applications.
76
In the example program in the next section, in which a servlet accesses a stateful session bean, the
session bean can be looked up using any of the following JNDI names:
java:global/LocalSessionBeanClient/StatefulSession1Bean
java:global/LocalSessionBeanClient/StatefulSession1Bean!
com.ivan.scbcd6.StatefulSession1Bean
java:app/LocalSessionBeanClient/StatefulSession1Bean
java:app/LocalSessionBeanClient/StatefulSession1Bean!
com.ivan.scbcd6.StatefulSession1Bean
java:module/StatefulSession1Bean
java:module/StatefulSession1Bean!com.ivan.scbcd6.StatefulSession1Bean
The JNDI name under which a session bean can be accessed may be altered by using a deployment
descriptor. All the above JNDI names assume that the EJB is deployed without a deployment
descriptor altering its JNDI name(s).
77
java.io.IOException;
java.io.PrintWriter;
java.util.ArrayList;
java.util.List;
import
import
import
import
import
import
import
import
javax.annotation.PostConstruct;
javax.naming.InitialContext;
javax.naming.NamingException;
javax.servlet.ServletException;
javax.servlet.annotation.WebServlet;
javax.servlet.http.HttpServlet;
javax.servlet.http.HttpServletRequest;
javax.servlet.http.HttpServletResponse;
import com.ivan.scbcd6.StatefulSession1Bean;
/**
* Servlet implementing a local EJB client.
*/
@WebServlet(name = "LocalEJBClientServlet", urlPatterns = "/test.do")
public class LocalEJBClientServlet extends HttpServlet
{
/* Constant(s): */
private static final long serialVersionUID = 1L;
/* Instance variable(s): */
private StatefulSession1Bean mLocalSessionBean;
/**
* Initializes the servlet after creation.
*/
@PostConstruct
public void intialize()
{
/*
* Use the JNDI API to look up a reference to the session bean
* the servlet is a client of.
* Any of the following JNDI names can be used:
* java:global/LocalSessionBeanClient/StatefulSession1Bean
* java:global/LocalSessionBeanClient/StatefulSession1Bean!
com.ivan.scbcd6.StatefulSession1Bean
* java:app/LocalSessionBeanClient/StatefulSession1Bean
* java:app/LocalSessionBeanClient/StatefulSession1Bean!
com.ivan.scbcd6.StatefulSession1Bean
* java:module/StatefulSession1Bean
* java:module/StatefulSession1Bean!com.ivan.scbcd6.StatefulSession1Bean
*/
try
{
InitialContext theInitialContext = new InitialContext();
mLocalSessionBean =
(StatefulSession1Bean)theInitialContext
.lookup("java:module/StatefulSession1Bean");
} catch (NamingException theException)
{
theException.printStackTrace();
}
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
@Override
protected void doGet(HttpServletRequest inRequest,
HttpServletResponse inResponse) throws ServletException, IOException
{
78
When running the example program as described earlier, it should produce result identical to that of
the previous example program with dependency injection.
In the servlet implementation class, note that:
There is a method named initialize which is annotated with the @PostConstruct annotation.
This method will be invoked by the servlet container immediately after the servlet instance
has been created and before it starts to serve requests.
In this method, a reference to the stateful session bean is looked up using the JNDI API.
As in the previously, any of the listed JNDI names can be used to retrieve a reference to the
stateful session bean.
You are encouraged to try some different JNDI names.
79
Non-Java clients.
The client view of a session bean can be mapped to for instance CORBA, allowing for
clients implemented in other languages.
Parameters and return values in the remote business interface of an EJB are passed by value see
the section on Session Bean Method Parameters for more information.
In the new project, create the session bean implementation class as below:
package com.ivan.scbcd6;
import
import
import
import
java.util.Date;
java.util.List;
javax.annotation.PostConstruct;
javax.ejb.Stateful;
/**
* Simplest possible stateful session bean exposing a remote business
* interface.
*/
@Stateful
public class StatefulSession1Bean implements StatefulSession1Remote
{
private static int sCurrentInstanceNumber = 1;
private int mInstanceNumber;
@PostConstruct
public void initialize()
{
mInstanceNumber = sCurrentInstanceNumber++;
System.out.println("*** StatefulSession1Bean " + mInstanceNumber +
" created.");
}
/**
* Creates a greeting to the person with the supplied name.
*
* @param inName Name of person to greet.
* @return Greeting.
*/
public String greeting(final String inName)
{
80
package com.ivan.scbcd6;
import java.util.List;
import javax.ejb.Remote;
/**
* Remote business interface of the StatefulSession1 EJB.
*/
@Remote
public interface StatefulSession1Remote
{
/**
* Creates a greeting to the person with the supplied name.
*
* @param inName Name of person to greet.
* @return Greeting.
*/
public String greeting(final String inName);
/**
* Processes the supplied list.
* The purpose of this method is to illustrate the difference
* in parameter passing semantics between local and remote EJBs.
*
* @param inList List to process.
*/
public void processList(List<String> inList);
}
Having deployed the project, we can in the server log see that the session bean can be accessed
using any of the following portable JNDI names:
81
...
INFO: Portable JNDI names for EJB StatefulSession1Bean :
[java:global/RemoteSessionBean/StatefulSession1Bean!
com.ivan.scbcd6.StatefulSession1Remote,
java:global/RemoteSessionBean/StatefulSession1Bean]
...
There are also a number of non-portable JNDI names, but we will not use those since they are
specific to GlassFish.
package com.ivan.scbcd6.client;
import
import
import
import
java.io.IOException;
java.io.PrintWriter;
java.util.ArrayList;
java.util.List;
import
import
import
import
import
import
javax.ejb.EJB;
javax.servlet.ServletException;
javax.servlet.annotation.WebServlet;
javax.servlet.http.HttpServlet;
javax.servlet.http.HttpServletRequest;
javax.servlet.http.HttpServletResponse;
import com.ivan.scbcd6.StatefulSession1Remote;
/**
* Servlet implementing a remote EJB client.
*/
@WebServlet(name = "RemoteEJBClientServlet", urlPatterns = "/test.do")
public class RemoteEJBClientServlet extends HttpServlet
{
/* Constant(s): */
private static final long serialVersionUID = 1L;
/* Instance variable(s): */
@EJB(lookup = "java:global/RemoteSessionBean/StatefulSession1Bean")
private StatefulSession1Remote mRemoteSessionBean;
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
@Override
protected void doGet(HttpServletRequest inRequest,
HttpServletResponse inResponse) throws ServletException, IOException
{
PrintWriter theResponseWriter = inResponse.getWriter();
String theRequestNameParam = inRequest.getParameter("name");
if (theRequestNameParam == null)
{
theRequestNameParam = "Anonymous";
82
}
String theResponse = mRemoteSessionBean.greeting(theRequestNameParam);
theResponseWriter.println("Response from the EJB: " + theResponse);
/* Process a list to examine parameter passing semantics. */
List<String> theList = new ArrayList<String>();
theList.add("string 1");
theList.add("string 2");
theList.add("last string");
mRemoteSessionBean.processList(theList);
/* Output list after EJB invocation. */
String theListStrings = "";
for (String theString : theList)
{
theListStrings += theString + ", ";
}
System.out.println("\nList after having invoked EJB processList: [" +
theListStrings + "]");
}
}
In the Package or Project Explorer in Eclipse, right-click the project and select Run As ->
Run on Server...
When being asked about which server to run the web application on, select GlassFish v3.
Response from the EJB: Hello Anonymous, I am stateful session bean 1. The time is now:
Mon Nov 08 17:17:43 CET 2010
...
INFO: *** StatefulSession1Bean 1 created.
INFO: StatefulSession1Bean.processList: The list contains: [string 1, string 2, last
string, ]
INFO: List after having invoked EJB processList: [string 1, string 2, last string, ]
Note that:
There is an instance field of the type StatefulSession1Remote that is annotated with an
@EJB annotation.
The type of the field is an interface type which is annotated with the @Remote annotation
the remote business view of the EJB.
In this case, the @EJB annotation annotates an instance field in which an EJB reference is to
be injected. More about injection of EJB references in the section on Dependency Injection
and Resource Lookup in chapter 14.
The @EJB annotation specifies a value for the lookup element.
As in the section on the @EJB annotation, this is the JNDI name used to resolve the
dependency to inject.
If both applications run in the same GlassFish v3 server, specifying the JNDI name is not
necessary.
The contents of the list passed as a parameter to the processList method of the EJB is the
same before and after the method call.
This confirms that parameters passed to the remote business view of an EJB is passed by
value, rather than by reference.
Thus the list is copied (rather: serialized and unserialized) when the method is invoked.
83
Copy the EJB's remote interface StatefulSession1Remote from the remote EJB project to the
package we just created.
package com.ivan.scbcd6.seclient;
import
import
import
import
import
java.util.ArrayList;
java.util.List;
javax.naming.InitialContext;
javax.naming.NamingException;
com.ivan.scbcd6.StatefulSession1Remote;
/**
* Remote Java SE EJB client that uses JNDI lookup to obtain references
* to EJBs.
*
* Note that the following JAR must be included on the client's class path:
* GlassFish 3: $GLASSFISH_HOME/modules/gf-client.jar
*/
public class RemoteSESessionBeanClient
{
/* Constant(s): */
private final static String REMOTE_EJB_JNDI =
"java:global/RemoteSessionBean/StatefulSession1Bean";
/* Instance variable(s): */
/** Remotely accessed EJB. */
private StatefulSession1Remote mRemoteSessionBean;
/**
* Looks up the remote EJB using its JNDI name.
*
* @throws NamingException If error occurs during lookup.
*/
private void lookupEJB() throws NamingException
{
InitialContext theContext = new InitialContext();
System.out.println("*** Starting Remote EJB Lookup...");
mRemoteSessionBean = (StatefulSession1Remote) theContext
.lookup(REMOTE_EJB_JNDI);
System.out.println("
84
}
/**
* Invokes the remote EJB and outputs results.
*/
private void invokedRemoteEJB()
{
String theResponse = mRemoteSessionBean.greeting("Java SE");
System.out.println("*** Response from the EJB: " +
theResponse);
/* Process a list to examine parameter passing semantics. */
List<String> theList = new ArrayList<String>();
theList.add("string 1");
theList.add("string 2");
theList.add("last string");
mRemoteSessionBean.processList(theList);
/* Output list after EJB invocation. */
String theListStrings = "";
for (String theString : theList)
{
theListStrings += theString + ", ";
}
System.out.println("*** List after having invoked EJB processList: [" +
theListStrings + "]");
}
public static void main(String[] args)
{
RemoteSESessionBeanClient theClient = new RemoteSESessionBeanClient();
try
{
theClient.lookupEJB();
theClient.invokedRemoteEJB();
} catch (Exception theException)
{
theException.printStackTrace();
}
}
}
85
Note that:
The GlassFish v3 client runtime library gf-client.jar must be included on the classpath.
Creating the InitialContext used to look up a reference to the remote EJB requires no
parameters.
This is possible as long as the client runs on the same computer as the GlassFish v3 server.
The JNDI lookup code is similar to that of the Local Session Bean Client with JNDI Lookup
seen earlier.
The only difference is that the type of the variable in which the reference to the EJB is stores
is the remote interface type.
86
Servlets.
Web services.
Java SE applications.
Other EJBs.
Dependency injection.
An instance field or setter-method in the client is annotated and the container ensures that
the appropriate reference is injected at runtime.
JNDI lookup.
Using the JNDI name of the bean, a reference to the bean is looked up programmatically
from within the client's code.
Please refer to the section on Dependency Injection and Resource Lookup in chapter 14 for more
information!
87
Comments
Remote
Local
Web Service
Compare the above with EJB 2.1 session beans, which also has remote and local home interfaces, in
addition to local and remote interfaces of the session beans.
It is possible for a session bean to have more than one client view; for instance it can give local
clients access to the no-interface view and expose its functionality as a web service.
88
5.3.3.Method Parameters
The developer of an EJB, as well as the developer of the client of the EJB, must take into
consideration the method parameters in the EJB. Whether an EJB is accessed remotely or locally
decides the parameter-passing semantics.
As mentioned earlier, parameters and return values of methods in the local client view are passed by
reference and will thus be shared between the client and the session bean. The following should
thus be avoided:
Objects being part of the state of a session bean must not be part of the return data.
Objects being part of the state of an EJB that are to contribute to the return data must be
copied (deep copy) and the copy included in the return data.
Objects being part of parameter data received when the session bean is invoked must not
become part of the state of a session bean.
Again, such objects must be copied (deep copy) and the copies may then be assigned to the
state of the EJB.
The risk of sharing references to objects between a client and an EJB does not exist with remotely
accessed EJBs, as the parameters and return values are passed by value.
However, the types of all parameters and return values of an EJB with a remote view must be
serializable.
EJB Reference Parameters and Return Values
89
In the package com.ivan.scbcd6, create the following four session bean implementation
classes:
package com.ivan.scbcd6;
import javax.ejb.EJB;
import javax.ejb.LocalBean;
import javax.ejb.Stateless;
@Stateless
@LocalBean
public class IdentityCheckingBean
{
@EJB
private StatelessSessionBean mStateless1;
@EJB
private StatelessSessionBean mStateless2;
@EJB
private StatefulSessionBean mStateful1;
@EJB
private StatefulSessionBean mStateful2;
@EJB
private SingletonSessionBean mSingleton1;
@EJB
private SingletonSessionBean mSingleton2;
@EJB
private StatelessSessionBeanLocalBusiness mStatelessLocalBusiness;
public void checkBeanIdentities()
{
/* Compare stateless bean with stateless bean. */
System.out.println("\n***** Compare stateless bean with stateless bean:");
doIdentityCheck(mStateless1, mStateless1, "Stateless1 equals stateless1");
doIdentityCheck(mStateless1, mStateless2, "Stateless1 equals stateless2");
doIdentityCheck(mStateless2, mStateless1, "Stateless2 equals stateless1");
doIdentityCheck(mStateless1, mStatelessLocalBusiness,
"Stateless1 equals stateless local business:");
/* Compare stateless bean with other kinds of beans. */
System.out.println("\n***** Compare stateless bean with other kinds of beans:");
doIdentityCheck(mStateless1, mStateful1, "Stateless1 equals stateful1");
doIdentityCheck(mStateless1, mSingleton1, "Stateless1 equals singleton1");
/* Compare stateful bean with stateful bean. */
System.out.println("\n***** Compare stateful bean with stateful bean:");
doIdentityCheck(mStateful1, mStateful1, "Stateful1 equals stateful1");
doIdentityCheck(mStateful1, mStateful2, "Stateful1 equals stateful2");
doIdentityCheck(mStateful2, mStateful1, "Stateful2 equals stateful1");
/* Compare stateful bean with other kinds of beans. */
System.out.println("\n***** Compare stateless bean with other kinds of beans:");
doIdentityCheck(mStateful1, mSingleton1, "Stateful1 equals singleton1");
/* Compare singleton bean with singleton bean. */
System.out.println("\n***** Compare singelton bean with singleton bean:");
doIdentityCheck(mSingleton1, mSingleton1, "Singleton1 equals singleton1");
doIdentityCheck(mSingleton1, mSingleton2, "Singleton1 equals singleton2");
doIdentityCheck(mSingleton2, mSingleton1, "Singleton2 equals singelton1");
90
}
public void checkBeanHashCodes()
{
/* Hash codes of stateless beans. */
System.out.println("\n***** Stateless bean hash codes:");
doBeanHashCode(mStateless1, "Stateless1");
doBeanHashCode(mStateless2, "Stateless2");
doBeanHashCode(mStatelessLocalBusiness, "Stateless local business");
/* Hash codes of stateful beans. */
System.out.println("\n***** Stateful bean hash codes:");
doBeanHashCode(mStateful1, "Stateful1");
doBeanHashCode(mStateful2, "Stateful2");
/* Hash codes of singleton beans. */
System.out.println("\n***** Singelton bean hash codes:");
doBeanHashCode(mSingleton1, "Singleton1");
doBeanHashCode(mSingleton2, "Singleton2");
}
private void doIdentityCheck(Object inBean1, Object inBean2,
String inMessage)
{
boolean theIdentityFlag;
theIdentityFlag = inBean1.equals(inBean2);
System.out.println("
" + inMessage + " " + theIdentityFlag);
}
private void doBeanHashCode(Object inBean, String inMessage)
{
int theHashCode = inBean.hashCode();
System.out.println("
}
}
package com.ivan.scbcd6;
import javax.annotation.PostConstruct;
import javax.ejb.LocalBean;
import javax.ejb.Stateless;
@Stateless
@LocalBean
public class StatelessSessionBean implements StatelessSessionBeanLocalBusiness
{
public StatelessSessionBean()
{
System.out.println("***** StatelessSessionBean created");
}
@PostConstruct
public void initialize()
{
System.out.println("***** StatelessSessionBean initialized");
}
}
package com.ivan.scbcd6;
import javax.annotation.PostConstruct;
import javax.ejb.LocalBean;
import javax.ejb.Stateful;
@Stateful
@LocalBean
public class StatefulSessionBean
{
public StatefulSessionBean()
{
System.out.println("***** StatefulSessionBean created");
91
}
@PostConstruct
public void initialize()
{
System.out.println("***** StatefulSessionBean initialized");
}
}
package com.ivan.scbcd6;
import javax.annotation.PostConstruct;
import javax.ejb.LocalBean;
import javax.ejb.Singleton;
@Singleton
@LocalBean
public class SingletonSessionBean
{
public SingletonSessionBean()
{
System.out.println("***** SingletonSessionBean created");
}
@PostConstruct
public void initialize()
{
System.out.println("***** SingletonSessionBean initialized");
}
}
package com.ivan.scbcd6;
import javax.ejb.Local;
@Local
public interface StatelessSessionBeanLocalBusiness
{
}
package com.ivan.scbcd6.client;
import java.io.IOException;
import java.io.PrintWriter;
import
import
import
import
import
import
javax.ejb.EJB;
javax.servlet.ServletException;
javax.servlet.annotation.WebServlet;
javax.servlet.http.HttpServlet;
javax.servlet.http.HttpServletRequest;
javax.servlet.http.HttpServletResponse;
import com.ivan.scbcd6.IdentityCheckingBean;
@WebServlet(name = "IdentityCheckingServlet", urlPatterns = "/checkidentity.do")
public class IdentityCheckingServlet extends HttpServlet
{
private static final long serialVersionUID = 1L;
@EJB
private IdentityCheckingBean mIdentityChecker;
@Override
protected void doGet(HttpServletRequest inRequest,
92
In the web browser, you should see the message from the above servlet.
In the GlassFish log you should see the following output, or similar, among other messages:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
*****
*****
*****
*****
*****
*****
*****
*****
*****
*****
*****
*****
*****
*****
StatefulSessionBean created
StatefulSessionBean created
StatefulSessionBean initialized
StatefulSessionBean created
StatefulSessionBean created
StatefulSessionBean initialized
Compare stateless bean with stateless bean:
Stateless1 equals stateless1 true
Stateless1 equals stateless2 true
Stateless2 equals stateless1 true
Stateless1 equals stateless local business: false
Compare stateless bean with other kinds of beans:
Stateless1 equals stateful1 false
Stateless1 equals singleton1 false
Compare stateful bean with stateful bean:
Stateful1 equals stateful1 true
Stateful1 equals stateful2 false
Stateful2 equals stateful1 false
Compare stateless bean with other kinds of beans:
Stateful1 equals singleton1 false
Compare singelton bean with singleton bean:
Singleton1 equals singleton1 true
Singleton1 equals singleton2 true
Singleton2 equals singelton1 true
Stateless bean hash codes:
Stateless1 hash code: 364806942
Stateless2 hash code: 364806942
Stateless local business hash code: 1157496838
Stateful bean hash codes:
Stateful1 hash code: 244462009
Stateful2 hash code: 536768552
Singelton bean hash codes:
Singleton1 hash code: 2037736064
Singleton2 hash code: 2037736064
The conclusions to draw from this example, which match the behaviour stipulated by the EJB 3.1
specification, are:
A reference to a session bean is equal to another, or the same, reference to the same session
bean, provided the references are to views of the same type.
This is true for all three types of session beans.
References to any two stateless session beans of the same type and to the same type of view
are always equal.
93
References to any two singleton session beans of the same type and to the same type of view
are always equal.
Hash codes of references to any two stateless session beans of the same type and to the same
type of view are always equal.
Hash codes of references to any two session beans to different type of views are always
different.
Hash codes of references to two different stateful session beans of the same type are always
different.
Hash codes of references to any two singleton session beans of the same type and to the
same type of view are always equal.
94
A system exception was thrown due to an error on the client side trying to call an EJB.
Any transaction will not be marked for rollback or rolled back.
A system exception was thrown due to an error occurring executing the EJB.
Any transaction will be marked for rollback by the container.
A recommendation that applies both in connection to application and system exceptions and
transactions is that an EJB client that receives an exception, be it an application or a system
exception, queries the transaction status prior to performing any lengthy, or in any other ways
costly, processing. This prevents doing unnecessary work that is later rolled back.
5.4.2.Application Exceptions
A client to an EJB that receives an application exception may continue to call the EJB.
Application exceptions from web service methods of stateless session beans are mapped to SOAP
faults specified in the WSDL. For details on how SOAP faults are mapped to exceptions on the
client side, please consult the JAX-WS specification.
5.4.3.System Exceptions
System exceptions may be thrown for the following reasons:
95
Transaction Status
Notes
RemoteException
See EJBException.
The following exceptions indicate that the transaction has been marked for rollback and will never commit.
EJBTransactionRolledba Rolled back or marked for
ckException
rollback.
Subclass of EJBException.
EJB invoked using EJB 3.1 view and EJB executing in client's
transaction.
Subclass of EJBException.
EJB invoked using EJB 2.1 local client view.
The following exceptions indicate that an attempt was made to invoke a method that require a client transaction
without the client having a transaction context.
These exceptions usually indicate an error in method transaction requirement declaration(s).
EJBTransactionRequired No transaction.
Exception
Subclass of EJBException.
EJB invoked using EJB 3.1 view.
TransactionRequiredLoc No transaction.
alException
Subclass of EJBException.
EJB invoked using EJB 2.1 local client view.
TransactionRequiredExc No transaction.
eption
The following exceptions indicate that an attempt was made to invoke a method on an EJB object that does not exist.
NoSuchEJBException
Subclass of EJBException.
EJB invoked using EJB 3.1 view.
Subclass of EJBException.
EJB invoked using EJB 2.1 local client view.
Subclass of RemoteException.
EJB invoked using EJB 2.1 remote client view.
RemoteException
Local EJB 2.1 interfaces and EJB 3.1 client view methods may not declare RemoteException.
96
In the package com.ivan.scbcd6, create the session bean implementation class according to
the following listing:
package com.ivan.scbcd6;
import
import
import
import
java.util.Date;
javax.annotation.PostConstruct;
javax.ejb.LocalBean;
javax.ejb.Singleton;
/**
* This class implements a singleton session bean with the ability
* to hold state.
*/
@Singleton
@LocalBean
public class SingletonSessionBeanA
{
/* Constant(s): */
private final static String BEAN_NAME = "SingletonSessionBeanA";
/* Instance variable(s): */
private String mStoredMessage = "[no message set]";
@PostConstruct
public void intialize()
{
System.out.println("*** " + BEAN_NAME + " - Initialized");
}
@PreDestroy
public void cleanUp()
{
System.out.println("*** " + BEAN_NAME + " - Destroyed");
97
}
public String retrieveMessage()
{
Date theCurrentTime = new Date();
return "Message from " + BEAN_NAME + " - " + mStoredMessage + " "
+ theCurrentTime;
}
public void storeMessage(final String inStoredMessage)
{
mStoredMessage = inStoredMessage;
}
}
In the com.ivan.scbcd6.client package, create the servlet that is to be the client of the
singleton session beans we just implemented:
package com.ivan.scbcd6.client;
import
import
import
import
import
import
import
import
java.io.IOException;
java.io.PrintWriter;
javax.ejb.EJB;
javax.servlet.ServletException;
javax.servlet.annotation.WebServlet;
javax.servlet.http.HttpServlet;
javax.servlet.http.HttpServletRequest;
javax.servlet.http.HttpServletResponse;
import com.ivan.scbcd6.SingletonSessionBeanA;
/**
* Servlet acting as a client of the singleton session bean.
*/
@WebServlet(name = "SingletonClientServlet", urlPatterns = "/test.do")
public class SingletonClientServlet extends HttpServlet
{
/* Constant(s): */
private static final long serialVersionUID = 1L;
private final static String STORE_ACTION = "store";
private final static String CLEAR_ACTION = "clear";
/* Instance variable(s): */
@EJB
private SingletonSessionBeanA mSingletonBeanA;
@Override
protected void doGet(HttpServletRequest inRequest,
HttpServletResponse inResponse) throws ServletException, IOException
{
System.out.println("**** Entering SingletonClientServlet");
String theRequestNameParam = inRequest.getParameter("name");
String theRequestActionParam = inRequest.getParameter("action");
/* Set default name if none provided. */
if (theRequestNameParam == null || theRequestNameParam.equals(""))
{
theRequestNameParam = "Anonymous Coward";
}
/* Generate output from servlet using session beans. */
PrintWriter theResponseWriter = inResponse.getWriter();
String theMessage;
theMessage = mSingletonBeanA.retrieveMessage();
theResponseWriter.println(theMessage);
/*
* Store or clear data in one of the singleton session beans
* if the supplied action so specifies.
*/
if (theRequestActionParam != null)
98
{
if (STORE_ACTION.equals(theRequestActionParam))
{
mSingletonBeanA.storeMessage(theRequestNameParam);
}
if (CLEAR_ACTION.equals(theRequestActionParam))
{
mSingletonBeanA.storeMessage("[CLEARED]");
}
}
System.out.println("**** Exiting SingletonClientServlet");
theResponseWriter.println("Finished invoking singleton session beans!");
}
}
If it is not the first time a request is issued to the above URL then there will be no initialization
message printed from the SingletonSessionBeanA.
All in all the program does what we expect it to do, but we cannot see any log messages.
99
package com.ivan.scbcd6;
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;
@Interceptor
public class LogInterceptor
{
public LogInterceptor()
{
System.out.println("LogInterceptor - Constructor");
}
@AroundInvoke
public Object logMethodEntryExit(InvocationContext inInvocationContext) throws
Exception
{
System.out.println("
LogInterceptor - Entering method: " +
inInvocationContext.getMethod().getName());
/* Invoke the intercepted method on the EJB and save the result. */
Object theResult = inInvocationContext.proceed();
System.out.println("
LogInterceptor - Exiting method: " +
inInvocationContext.getMethod().getName());
/* Return the result from the intercepted method. */
return theResult;
}
}
...
@Singleton
@LocalBean
@Interceptors(LogInterceptor.class)
public class SingletonSessionBeanA
{
/* Constant(s): */
...
100
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
Note that:
Business methods, like retrieveMessage and storeMessage, of the SingletonSessionBeanA
are intercepted.
PreDestroy and PostConstruct methods of the SingletonSessionBeanA are not intercepted.
101
In the package com.ivan.scbcd6, add the Java class that implements the default interceptor.
package com.ivan.scbcd6;
import javax.interceptor.InvocationContext;
public class MyDefaultInterceptor
{
public Object aroundInvoke(InvocationContext inInvocationContext) throws Exception
{
System.out.println("
MyDefaultInterceptor intercepting: " +
inInvocationContext.getTarget().getClass().getSimpleName() +
"." + inInvocationContext.getMethod().getName());
return inInvocationContext.proceed();
}
}
102
<interceptor-class>com.ivan.scbcd6.MyDefaultInterceptor</interceptor-class>
</interceptor-binding>
</assembly-descriptor>
</ejb-jar>
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
Note that:
The default interceptor class does not contain any annotations.
The <interceptor> element in the ejb-jar.xml deployment descriptor specifies a class as
being an interceptor class and which method is to be the around-invoke interceptor method.
One <interceptor-binding> element is used to link an interceptor class to the target EJB(s)
which method calls are to be intercepted.
When specifying the name of the target EJB in the <interceptor-binding> element, we used
the wildcard character *.
This means that all the EJBs in the same module (ejb-jar file or .war file) are to be
intercepted.
Looking at the console output, we can see that the default interceptor is invoked before the
log interceptor.
We will look more closely at the order in which interceptors are invoked later in this chapter.
We see that it is possible to retrieve the name of the target object and the method that is
intercepted.
103
Modify the LogInterceptor class to inherit from the LogInterceptorSuperclass we just added.
...
@Interceptor
public class LogInterceptor extends LogInterceptorSuperclass
{
...
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
104
Note that:
The LogInterceptorSuperclass looks, and indeed is, a regular interceptor class.
We did not modify the list of interceptor classes in the @Interceptors annotation annotating
the SingletonSessionBeanA class.
The LogInterceptorSuperclass is not included in the list of interceptors specified in the
@Interceptors annotation in the SingletonSessionBeanA class.
In the console output, we can see that the LogInterceptorSuperclass interceptor is invoked
after the MyDefaultInterceptor interceptor and before the LogInterceptor interceptor.
We will look more closely at the order in which interceptors are invoked later in this chapter.
...
public class MyDefaultInterceptor
{
public Object aroundInvoke(InvocationContext inInvocationContext) throws Exception
{
...
}
@PostConstruct
public void postConstruct(InvocationContext inInvocationContext)
throws Exception
{
System.out.println("
MyDefaultInterceptor.postConstruct");
/*
* Important!
* Must call proceed, in order for the other interceptor methods
* in the interceptor chain to become invoked.
*/
inInvocationContext.proceed();
}
}
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
105
Note that:
The post-construct life-cycle event interceptor method in the default interceptor class is
annotated with the @PostConstruct annotation.
Life-cycle event interceptor methods, as all interceptor methods, can be located either in
separate interceptor classes or in the target class (or both).
The post-construct life-cycle event interceptor in the default interceptor class is called the
first time a request is issued to the URL, but not for subsequent requests.
This matches the expected behaviour of our application the singleton session bean in our
example application is created once and once only.
The post-construct life-cycle event interceptor method in the default interceptor class
invokes the proceed() method on the InvocationContext object supplied as a parameter.
This is to ensure that post-construct life-cycle event interceptor methods in other interceptor
classes are also invoked.
The post-construct life-cycle event interceptor method in the EJB bean class
SingeltonSessionBeanA is called once, after the post-construct life-cycle event interceptor
method in the default interceptor class has been invoked.
We'll examine interceptor invocation order in a subsequent section of this chapter.
106
...
@Schedule(second="*/5", minute="*", hour="*")
public void doPeriodic()
{
System.out.println("*** Do periodic: " + (new Date()));
}
...
...
INFO:
INFO:
INFO:
INFO:
...
***
***
***
***
Do
Do
Do
Do
periodic:
periodic:
periodic:
periodic:
Mon
Mon
Mon
Mon
Sep
Sep
Sep
Sep
13
13
13
13
07:05:45
07:05:50
07:05:55
07:06:00
CEST
CEST
CEST
CEST
2010
2010
2010
2010
...
@AroundTimeout
public Object logTimeout(InvocationContext inInvocationContext)
throws Exception
{
System.out.println("
LogInterceptor - Entering timeout: "
+ inInvocationContext.getMethod().getName());
Object theResult = inInvocationContext.proceed();
System.out.println("
LogInterceptor - Exiting timeout: "
+ inInvocationContext.getMethod().getName());
return theResult;
}
...
...
INFO:
LogInterceptor - Entering timeout: doPeriodic
INFO: *** Do periodic: Mon Sep 13 07:11:35 CEST 2010
INFO:
LogInterceptor - Exiting timeout: doPeriodic
INFO:
LogInterceptor - Entering timeout: doPeriodic
INFO: *** Do periodic: Mon Sep 13 07:11:40 CEST 2010
INFO:
LogInterceptor - Exiting timeout: doPeriodic
INFO:
LogInterceptor - Entering timeout: doPeriodic
INFO: *** Do periodic: Mon Sep 13 07:11:45 CEST 2010
INFO:
LogInterceptor - Exiting timeout: doPeriodic
...
107
Note that:
The timeout method interceptor is annotated with the @AroundTimeout annotation.
The signature of the timeout method interceptor is identical to the signature of business
method interceptors, that is:
Object someMethodName(InvocationContext) throws Exception
The timeout method interceptor uses the InvocationContext.proceed method to invoke the
timeout callback method.
108
All the different interceptor methods have the following common properties:
Can access the same components and resources as the method being intercepted.
For life-cycle event interceptors, see the different sections on operations allowed in the
different bean types; stateful session beans, stateless session beans, singleton session beans,
message driven beans.
Answer
@AroundInvoke
Object someMethodName(InvocationContext)
throws Exception
109
Answer
@AroundTimeout
Object someMethodName(InvocationContext)
throws Exception
110
Answer
Unspecified.
System exceptions.
111
112
113
114
115
This figure shows the parts of the ejb-jar.xml deployment descriptors relevant to specifying
interceptors:
116
6.10.
The following specifies the order in which interceptors are invoked. This applies to all the different
interceptor types with the corresponding interceptor methods:
Default interceptors.
Any default interceptors are invoked first in the order in which they are declared in the ejbjar.xml deployment descriptor.
117
Note that:
The invocation order specified by annotations may be overridden using the ejb-jar.xml
deployment descriptor.
Interceptor methods overridden in a subclass will not be invoked, regardless of whether the
overriding method is an interceptor method or not.
Important note!
When there are multiple interceptors of the same kind, for instance for postconstruct life-cycle events, a interceptor chain is built and the first interceptor
method in the chain is invoked by the container.
If an interceptor method takes an InvocationContext object parameter, then the
proceed() method must be invoked in this object, in order for the other interceptors
in the chain to be invoked. This applies to all kinds of interceptor methods!
javax.annotation.PostConstruct;
javax.annotation.Resource;
javax.ejb.AsyncResult;
javax.ejb.Asynchronous;
javax.ejb.LocalBean;
javax.ejb.SessionContext;
javax.ejb.Stateful;
/**
* Stateful session bean with asynchronous methods.
*/
@Stateful
@LocalBean
public class AsynchStatefulSessionBean
{
@Resource
private SessionContext mSessionContext;
@PostConstruct
public void initialize()
{
System.out.println("***** AsynchStatefulSessionBean initialized");
}
118
@Asynchronous
public Future<String> asynchWithException() throws Exception
{
/* Trick the compiler to accept the method always throws an exception. */
int i = 1;
if (i < 2)
{
System.out
.println("***** AsynchStatefulSessionBean asynchWithException throwing
exception");
throw new Exception("Exception from AsynchStatefulSessionBean");
}
return new AsyncResult<String>("Never happens");
}
@Asynchronous
public Future<String> slowAsynch()
{
String theResult = (new Date()).toString();
waitSomeTime(5000L);
System.out
.println("***** AsynchStatefulSessionBean Exiting slowAsynch");
return new AsyncResult<String>(theResult);
}
@Asynchronous
public void slowOneWayAsynch()
{
waitSomeTime(5000L);
System.out
.println("***** AsynchStatefulSessionBean Exiting slowOneWayAsynch");
}
@Asynchronous
public Future<String> canBeCancelled()
{
String theResult = "Not cancelled " + new Date();
for (int i = 1; i < 100; i++)
{
waitSomeTime(100L);
System.out
.println("***** AsynchStatefulSessionBean canBeCancelled waited "
+ i);
/* Check if client attempted to cancel the method. */
if (mSessionContext.wasCancelCalled())
{
theResult = "Cancelled " + new Date();
break;
}
}
return new AsyncResult<String>(theResult);
}
private void waitSomeTime(long inDelayInMillisec)
{
try
{
Thread.sleep(inDelayInMillisec);
} catch (InterruptedException theException)
{
// Ignore exceptions.
}
}
}
package com.ivan.scbcd6.client;
import
import
import
import
java.io.IOException;
java.io.PrintWriter;
java.util.concurrent.ExecutionException;
java.util.concurrent.Future;
import
import
import
import
import
import
javax.ejb.EJB;
javax.servlet.ServletException;
javax.servlet.annotation.WebServlet;
javax.servlet.http.HttpServlet;
javax.servlet.http.HttpServletRequest;
javax.servlet.http.HttpServletResponse;
import com.ivan.scbcd6.beans.AsynchStatefulSessionBean;
@WebServlet(name = "AsynchClientServlet", urlPatterns = "/asynch.do")
public class AsynchClientServlet extends HttpServlet
{
private static final long serialVersionUID = 1L;
@EJB
private AsynchStatefulSessionBean mAsynchBean;
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
@Override
protected void doGet(HttpServletRequest inRequest,
HttpServletResponse inResponse) throws ServletException, IOException
{
System.out.println("**** Entering AsynchClientServlet");
Future<String> theSlowAsynchResult = null;
Future<String> theAsynchWithExceptionResult = null;
Future<String> theCanBeCancelledResult = null;
PrintWriter theResponseWriter = inResponse.getWriter();
try
{
/* Call void asynchronous method. */
System.out
.println("***** AsynchClientServlet - About to call slowOneWayAsynch");
mAsynchBean.slowOneWayAsynch();
System.out
.println("***** AsynchClientServlet - Finished calling
slowOneWayAsynch");
/* Call slow asynchronous method. */
System.out
.println("***** AsynchClientServlet - About to call slowAsynch");
theSlowAsynchResult = mAsynchBean.slowAsynch();
System.out
.println("***** AsynchClientServlet - Finished calling slowAsynch");
/* Call asynchronous method that will throw an exception. */
System.out
.println("***** AsynchClientServlet - About to call
asynchWithException");
theAsynchWithExceptionResult = mAsynchBean.asynchWithException();
System.out
.println("***** AsynchClientServlet - Finished calling
asynchWithException");
/* Call asynchronous method that can be canceled and cancel it. */
System.out
.println("***** AsynchClientServlet - About to call canBeCancelled");
theCanBeCancelledResult = mAsynchBean.canBeCancelled();
System.out
.println("***** AsynchClientServlet - Finished calling canBeCancelled");
waitSomeTime((long)(Math.random() * 1000) + 1000L);
System.out
.println("***** AsynchClientServlet - About to cancel canBeCancelled");
120
theCanBeCancelledResult.cancel(true);
System.out
.println("***** AsynchClientServlet - Cancelled canBeCancelled");
} catch (Exception theException)
{
System.out
.println("***** AsynchClientServlet - An exeption was thrown: "
+ theException.getMessage());
}
try
{
/* Retrieve results from asynchronous invocations. */
System.out
.println("\n***** AsynchClientServlet - slowAsynch result: "
+ theSlowAsynchResult.get());
System.out
.println("***** AsynchClientServlet - canBeCancelled result: "
+ theCanBeCancelledResult.get());
/* Wait for asynchWithException to complete. */
while (!theAsynchWithExceptionResult.isDone())
{
System.out.println("
Waiting...");
}
System.out
.println("***** AsynchClientServlet - asynchWithException result: "
+ theAsynchWithExceptionResult);
theAsynchWithExceptionResult.get();
} catch (InterruptedException e)
{
System.out.println("***** AsynchClientServlet - "
+ "An InterruptedException was thrown");
} catch (ExecutionException e)
{
System.out.println("***** AsynchClientServlet - "
+ "An ExecutionException was thrown: " + e);
}
System.out.println("**** Exiting AsynchClientServlet");
theResponseWriter
.println("Finished invoking asynchronous session bean!");
}
private void waitSomeTime(long inDelayInMillisec)
{
try
{
Thread.sleep(inDelayInMillisec);
} catch (InterruptedException theException)
{
// Ignore exceptions.
}
}
}
Wait until the view in the browser reads Finished invoking asynchronous session bean!.
In the GlassFish log you should see the following output, or similar, among other messages:
INFO:
INFO:
INFO:
INFO:
INFO:
call slowOneWayAsynch
calling slowOneWayAsynch
call slowAsynch
calling slowAsynch
121
INFO:
INFO:
INFO:
INFO:
*****
*****
*****
*****
AsynchClientServlet
AsynchClientServlet
AsynchClientServlet
AsynchClientServlet
About to
Finished
About to
Finished
call asynchWithException
calling asynchWithException
call canBeCancelled
calling canBeCancelled
Note that:
A number of methods in the session bean implementation class are annotated with the
@Asynchronous annotation.
This annotation demarcates an asynchronous method in an EJB.
All the methods annotated with @Asynchronous has a return type void or Future<T>,
where T is the type of the result produced by the method.
All the asynchronous methods that has a return type Future<T> creates an object of the type
AsynchResult<T>, where T is the type of the result produced by the method.
The method asynchWithException declares and throws an exception of the type Exception.
The slightly superfluous if-clause in this method is there in order to trick the compiler into
accepting a method that always throws an exception.
The client calling the canBeCancelled method calls the Future.cancel(true) method in order
to attempt to cancel the method call.
If the parameter value had been false, then any call already in progress would have been
allowed to continue until completion.
If the session bean had not checked the cancel-flag in the SessionContext using the
wasCancelCalled method, then the method would have continued until completion.
We can see from the console output that all asynchronous method invocations immediately
returns and the client is free to continue with its duties. The methods later completes and
prints an exit-message.
This is of course the normal and expected behaviour when invoking asynchronous
operations.
The results from asynchronous methods that produce a result is retrieved by the client by
invoking the Future.get method.
The ExecutionException thrown as the result of the asynchronous method in the session
bean thrown an exception wraps the exception thrown by the session bean.
This concludes this example. In subsequent sections we will look at what the EJB specification says
about asynchronous session beans. Finally, we will summarize the chapter by listing the
modifications that are required in order for method(s) of a session bean to become asynchronous.
Using the <async-method> element when declaring a session bean in the deployment
descriptor.
This causes one or more business methods in the session bean to become asynchronous.
123
Denoting a method as being asynchronous is not enough, as we will see in the next section.
NOT_SUPPORTED
REQUIRED
SUPPORTS
REQUIRES_NEW
MANDATORY
NEVER
A method without a transaction context will throw an exception if an attempt is made to, for
instance, invoke the SessionContext.getRollbackOnly() method or other methods that assume the
presence of a transaction context.
124
Exception
Comments
Preparation of asynchronous
method dispatch.
EJBException or
RemoteException (if business
interface extends Remote)
Execution of asynchronous
method.
Application (checked) exceptions. Methods with void return type are not allowed
to declare any checked exceptions.
No exceptions propagate to the client.
Execution of asynchronous
method.
Determine wether the operation completed abnormally and any associated exception.
The container may throw a system exception of the type EJBException as a result of calling the
asynchronous method if there are any problems when preparing to dispatch the call.
The table below lists the methods of a Future<V> object and describes their semantics in when
used in connection with asynchronous invocation of session beans:
Method Signature
Description
boolean isCancelled()
boolean isDone()
126
Point-to-point destination.
A queue destination. Retains messages until they are consumed by a (single) message
consumer. Note that several consumers may be registered on the same queue, but in such a
case the consumer that will receive the message is randomly chosen.
Publish/subscribe destination.
A topic destination. Immediately forwards messages sent by the message producer to all the
consumers registered for the topic. In default configuration, the topic does not retain
messages thus messages sent to a topic that has no consumers will be lost.
JMS allows for durable subscription to topic destinations, which means that messages will
be retained until the consumer will become active.
Before going deeper into detailed information on message driven beans, we'll first look at two
example programs. The first one shows message driven beans listening to a topic and the second
one shows message driven beans listening to a queue.
A word of caution:
When developing message driven beans, be clear about what beans are listening to
which queues or topics.
Example: Application A that produces messages to a topic will cause message driven
beans in application B listening to the same topic to receive messages.
This may have unwanted side effects.
127
Having created two physical destinations in GlassFish for the message driven bean example programs.
Having created two JMS connection factories in GlassFish for the message driven bean example programs.
128
Having created two JMS destination resources in GlassFish for the message driven bean example programs.
129
In the package com.ivan.scbcd6.entity, create the message bean class. A message holds a
message string, a message number and a timestamp.
package com.ivan.scbcd6.entity;
import java.io.Serializable;
import java.util.Date;
/**
* Represents a message sent by a producer to the topic.
*/
public class MyMessage implements Serializable
{
/* Constant(s): */
private static final long serialVersionUID = -4682924711829199796L;
/* Instance variable(s): */
private String mMessageString = "";
private long mMessageNumber;
private Date mMessageTime;
public String getMessageString()
{
return mMessageString;
}
public void setMessageString(final String inMessageString)
{
mMessageString = inMessageString;
}
public long getMessageNumber()
{
return mMessageNumber;
}
public void setMessageNumber(final long inMessageNumber)
{
mMessageNumber = inMessageNumber;
}
public Date getMessageTime()
{
return mMessageTime;
}
public void setMessageTime(final Date inMessageTime)
{
mMessageTime = inMessageTime;
}
}
Note that:
package com.ivan.scbcd6.ejbs;
import java.util.concurrent.atomic.AtomicInteger;
import
import
import
import
import
import
javax.ejb.ActivationConfigProperty;
javax.ejb.MessageDriven;
javax.jms.JMSException;
javax.jms.Message;
javax.jms.MessageListener;
javax.jms.ObjectMessage;
import com.ivan.scbcd6.entity.MyMessage;
/**
* Message driven bean listening to a topic.
*/
@MessageDriven(activationConfig =
{
@ActivationConfigProperty(propertyName = "destinationType", propertyValue =
"javax.jms.Topic")
}, mappedName = "jms/TopicDestination", name="TopicListener1")
public class TopicListenerEJB implements MessageListener
{
/* Constant(s): */
/* Class variable(s): */
private static AtomicInteger mCurrentBeanNumber = new AtomicInteger(0);
/* Instance variable(s): */
private int mBeanNumber = mCurrentBeanNumber.incrementAndGet();
/**
* Default constructor.
*/
public TopicListenerEJB()
{
System.out.println("*** TopicListenerEJB created: " + mBeanNumber);
}
/**
* @see MessageListener#onMessage(Message)
*/
@Override
public void onMessage(Message inMessage)
{
System.out.println("*** Bean " + mBeanNumber + " received message: "
+ inMessage);
extractMessagePayload(inMessage);
}
private void extractMessagePayload(Message inMessage)
{
/* Extract the message payload, if any. */
if (inMessage instanceof ObjectMessage)
{
try
{
ObjectMessage theObjMsg = (ObjectMessage)inMessage;
MyMessage theMsgPayload = (MyMessage)theObjMsg.getObject();
System.out.println("Received message with number: " +
theMsgPayload.getMessageNumber());
System.out.println("
Message string: " +
theMsgPayload.getMessageString());
131
System.out.println("
Message time: " +
theMsgPayload.getMessageTime());
} catch (JMSException theException)
{
System.out.println(
"An error occurred retrieving message payload: " +
theException);
}
}
}
}
Note that:
Before extracting the payload of the message, the JMS message is first examined to
determine if it is of the type ObjectMessage.
There are a number of different types of payload that can be enclosed with JMS messages,
please refer to the API documentation of javax.jms.Message for more information.
Nowhere is the acknowledge method called on the JMS message instance received.
The EJB 3.1 specification explicitly states that message driven beans should not use the JMS
API for message acknowledgement. This is a duty that is to be handled by the EJB
container.
132
Create a file named ejb-jar.xml in the WebContent/WEB-INF directory with the following
content:
Note that:
The ejb-jar.xml deployment descriptor contains definitions of three message driven beans.
The name of the EJBs are TopicListener1, TopicListener2 and TopicListener3.
All the EJB definitions in the ejb-jar.xml deployment descriptor uses the same
implementation class.
The first message driven bean definition in the ejb-jar.xml deployment descriptor has the
same name as the name specified in the @MessageDriven annotation in the EJB
implementation class.
This EJB definition is not strictly necessary, since the EJB is already defined using
annotations. The definition in the deployment descriptor can be used to override the
configuration in the annotations or to add additional configuration parameters.
133
package com.ivan.scbcd6.producer;
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
java.io.IOException;
java.io.PrintWriter;
java.util.Date;
java.util.concurrent.atomic.AtomicLong;
javax.annotation.Resource;
javax.jms.Connection;
javax.jms.ConnectionFactory;
javax.jms.JMSException;
javax.jms.MessageProducer;
javax.jms.ObjectMessage;
javax.jms.Session;
javax.jms.Topic;
javax.servlet.ServletException;
javax.servlet.annotation.WebServlet;
javax.servlet.http.HttpServlet;
javax.servlet.http.HttpServletRequest;
javax.servlet.http.HttpServletResponse;
import com.ivan.scbcd6.entity.MyMessage;
/**
* Servlet producing JMS messages when accessed.
*/
@WebServlet(name="MessageProducerServlet", urlPatterns="/sendmsg.do")
public class MessageProducerServlet extends HttpServlet
{
/* Constant(s): */
private static final long serialVersionUID = 1647640647915937983L;
/* Instance variable(s): */
/** Connection factory for topic. */
@Resource(mappedName = "jms/TopicConnectionFactory")
private ConnectionFactory mTopicConnectionFactory;
/** Topic destination. */
@Resource(mappedName = "jms/TopicDestination")
private Topic mTopicDestination;
/** MyMessage number counter. */
private AtomicLong mMessageNumber = new AtomicLong(0);
/**
* @see HttpServlet#HttpServlet()
*/
public MessageProducerServlet()
{
super();
System.out.println("*** MessageProducerServlet created");
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
@Override
protected void doGet(HttpServletRequest inRequest,
HttpServletResponse inResponse) throws ServletException, IOException
{
sendJmsMessage();
PrintWriter theResponseWriter = inResponse.getWriter();
theResponseWriter.println("A message was sent at " + new Date());
}
134
135
Looking in the console, you can see the log output from the example program:
Note that:
An instance of the message-producing servlet is created.
Three instances of the message driven bean listening to the topic are created.
Each of the three message driven beans receive a copy of the message sent to the topic.
This concludes the topic message driven example.
136
In the package com.ivan.scbcd6.entity, create the message bean class MyMessage. The class
is identical to that used in the previous example, so no code listing is provided here.
javax.ejb.ActivationConfigProperty;
javax.ejb.MessageDriven;
javax.jms.JMSException;
javax.jms.Message;
javax.jms.MessageListener;
javax.jms.ObjectMessage;
import com.ivan.scbcd6.entity.MyMessage;
/**
* Message driven bean listening to a queue.
*/
@MessageDriven(activationConfig =
{
@ActivationConfigProperty(propertyName = "destinationType", propertyValue =
"javax.jms.Queue")
}, mappedName = "jms/QueueDestination", name="QueueListener1")
public class QueueListenerEJB implements MessageListener
{
/* Constant(s): */
/* Class variable(s): */
private static AtomicInteger mCurrentBeanNumber = new AtomicInteger(0);
/* Instance variable(s): */
private int mBeanNumber = mCurrentBeanNumber.incrementAndGet();
/**
* Default constructor.
*/
public QueueListenerEJB()
{
System.out.println("*** QueueListenerEJB created: " + mBeanNumber);
}
/**
* @see MessageListener#onMessage(Message)
*/
@Override
137
The following notes are almost identical to those of the message driven bean in the topic example
above.
Note that:
Before extracting the payload of the message, the JMS message is first examined to
determine if it is of the type ObjectMessage.
There are a number of different types of payload that can be enclosed with JMS messages,
please refer to the API documentation of javax.jms.Message for more information.
Nowhere is the acknowledge method called on the JMS message instance received.
The EJB 3.1 specification explicitly states that message driven beans should not use the JMS
API for message acknowledgement. This is a duty that is to be handled by the EJB
container.
Create a file named ejb-jar.xml in the WebContent/WEB-INF directory with the following
content:
139
Note that:
The ejb-jar.xml deployment descriptor contains definitions of three message driven beans.
The name of the EJBs are QueueListener1, QueueListener2 and QueueListener3.
All the EJB definitions in the ejb-jar.xml deployment descriptor uses the same
implementation class.
The first message driven bean definition in the ejb-jar.xml deployment descriptor has the
same name as the name specified in the @MessageDriven annotation in the EJB
implementation class.
This EJB definition is not strictly necessary, since the EJB is already defined using
annotations. The definition in the deployment descriptor can be used to override the
configuration in the annotations or to add additional configuration parameters.
package com.ivan.scbcd6.producer;
import
import
import
import
java.io.IOException;
java.io.PrintWriter;
java.util.Date;
java.util.concurrent.atomic.AtomicLong;
import
import
import
import
import
import
import
import
import
import
import
import
import
javax.annotation.Resource;
javax.jms.Connection;
javax.jms.ConnectionFactory;
javax.jms.JMSException;
javax.jms.MessageProducer;
javax.jms.ObjectMessage;
javax.jms.Queue;
javax.jms.Session;
javax.servlet.ServletException;
javax.servlet.annotation.WebServlet;
javax.servlet.http.HttpServlet;
javax.servlet.http.HttpServletRequest;
javax.servlet.http.HttpServletResponse;
import com.ivan.scbcd6.entity.MyMessage;
/**
* Servlet producing JMS messages when accessed.
*/
@WebServlet(name="MessageProducerServlet", urlPatterns="/sendmsg.do")
public class MessageProducerServlet extends HttpServlet
{
/* Constant(s): */
private static final long serialVersionUID = -4364474814559146703L;
/* Instance variable(s): */
/** Connection factory for queue. */
@Resource(mappedName = "jms/QueueConnectionFactory")
private ConnectionFactory mQueueConnectionFactory;
/** Queue destination. */
@Resource(mappedName = "jms/QueueDestination")
private Queue mQueueDestination;
/** MyMessage number counter. */
private AtomicLong mMessageNumber = new AtomicLong(0);
/**
* @see HttpServlet#HttpServlet()
140
*/
public MessageProducerServlet()
{
super();
System.out.println("*** MessageProducerServlet created");
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
@Override
protected void doGet(HttpServletRequest inRequest,
HttpServletResponse inResponse) throws ServletException, IOException
{
PrintWriter theResponseWriter = inResponse.getWriter();
try
{
sendJmsMessage();
theResponseWriter.println("A message was sent at " + new Date());
} catch (JMSException theException)
{
theResponseWriter.println("An error occurred sending message: " +
theException);
}
}
private void sendJmsMessage() throws JMSException
{
MessageProducer theJMSMessageProducer = null;
Connection theJMSConnection = null;
try
{
/* Retrieve a JMS connection from the queue connection factory. */
theJMSConnection = mQueueConnectionFactory.createConnection();
/*
* Create the JMS session; not transacted and with auto* acknowledge.
*/
Session theJMSSession =
theJMSConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
/* Create a JMS message producer for the queue destination. */
theJMSMessageProducer =
theJMSSession.createProducer(mQueueDestination);
/* Create the object to be sent in the message created above. */
MyMessage theObjectToSend = new MyMessage();
theObjectToSend.setMessageNumber(mMessageNumber.incrementAndGet());
theObjectToSend.setMessageString("Hello Message Driven Beans");
theObjectToSend.setMessageTime(new Date());
/* Create message used to send a Java object. */
ObjectMessage theJmsObjectMessage =
theJMSSession.createObjectMessage();
theJmsObjectMessage.setObject(theObjectToSend);
/* Send the message. */
theJMSMessageProducer.send(theJmsObjectMessage);
} finally
{
closeJmsResources(theJMSConnection);
}
}
/**
* Closes the supplied JMS connection if it is not null.
* If supplied connection is null, then do nothing.
*
* @param inJMSConnection JMS connection to close.
*/
private void closeJmsResources(Connection inJMSConnection)
{
if (inJMSConnection != null)
{
141
try
{
inJMSConnection.close();
} catch (JMSException theException)
{
// Ignore exceptions.
}
}
}
}
142
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
Note that:
An instance of the message-producing servlet is created.
Instances of the message driven beans listening to the queue are created as needed, that is,
immediately prior to being dispatched a message.
Each message sent to the queue are received and handled by one single message driven bean
instance only.
In the example above, the first message is handled by bean number one, the second message
is handled by bean number two and the third message is handled by bean number three.
This concludes the queue message driven example.
143
144
145
Description
destinationType
acknowledgeMode
messageSelector
subscriptionDurability
146
Note that:
The <activation-config> element in which the messaging configuration is specified appears
in the <message-driven> element that defines the message driven to which the messaging
configuration applies.
147
148
Message driven beans should not throw RuntimeException-s from any method.
Runtime exceptions will cause the message driven bean to be destroyed by the container.
If the message driven bean throwing a runtime exception uses bean-managed transactions,
the container should not acknowledge the message.
The figures in this section show how exceptions from message driven bean methods are handled,
how transactions are affected and what exceptions clients receives.
Omitted from the diagrams are logging to the container's log, which always takes place in the case
of system exceptions.
149
150
8.10.
151
8.11.
Transaction Context
Undefined
Undefined
Listener method(s)
Constructor
Undefined
Undefined
Undefined
8.11.1.
Transaction Attributes
In message driven beans with container managed transactions, methods that support transactions
must use transactions attributes as follows:
Listener method(s)
REQUIRED or NOT_SUPPORTED
152
8.12.
153
8.13.
Constructor
Allowed Operations
None.
MessageDrivenContext:
lookup,
getContextData,
getTimerService,
getUserTransaction (BMT only).
JNDI Access: Available
EntityManagerFactory: Accessible.
MessageDrivenContext:
getCallerPrincipal,
isCallerInRole,
lookup,
getContextData,
getUserTransaction (BMT only),
getRollbackOnly (CMT only),
setRollbackOnly (CMT only).
JNDI Access: Available
Resource managers: Accessible.
Other EJBs: Accessible.
EntityManagerFactory: Accessible.
EntityManager: Accessible.
Timer and TimerService methods: Accessible.
UserTransaction methods: Accessible (BMT
only).
MessageDrivenContext:
getCallerPrincipal,
lookup,
154
getContextData,
getUserTransaction (BMT only),
getRollbackOnly (CMT only),
setRollbackOnly (CMT only).
JNDI Access: Available
Resource managers: Accessible.
Other EJBs: Accessible.
EntityManagerFactory: Accessible.
EntityManager: Accessible.
Timer and TimerService methods: Accessible.
UserTransaction methods: Accessible (BMT
only).
In addition, the getRollbackOnly and setRollbackOnly methods in the MessageDrivenContext
interface may only be called in methods of message driven beans that execute in a transaction
context.
155
java.io.IOException;
java.io.PrintWriter;
java.util.Date;
java.util.concurrent.atomic.AtomicLong;
import
import
import
import
import
import
import
import
import
import
import
import
import
javax.annotation.Resource;
javax.jms.Connection;
javax.jms.ConnectionFactory;
javax.jms.JMSException;
javax.jms.MessageProducer;
javax.jms.ObjectMessage;
javax.jms.Queue;
javax.jms.Session;
javax.servlet.ServletException;
javax.servlet.annotation.WebServlet;
javax.servlet.http.HttpServlet;
javax.servlet.http.HttpServletRequest;
javax.servlet.http.HttpServletResponse;
import com.ivan.scbcd6.entity.MyMessage;
/**
* Servlet producing JMS messages when accessed.
*/
@WebServlet(name="MessageProducerServlet", urlPatterns="/sendmsg.do")
public class MessageProducerServlet extends HttpServlet
{
/* Constant(s): */
private static final long serialVersionUID = -4364474814559146703L;
/* Instance variable(s): */
/** Connection factory for queue. */
@Resource(mappedName = "jms/QueueConnectionFactory")
private ConnectionFactory mQueueConnectionFactory;
/** Queue destination. */
@Resource(mappedName = "jms/QueueDestination")
private Queue mQueueDestination;
/** MyMessage number counter. */
private AtomicLong mMessageNumber = new AtomicLong(0);
/**
* @see HttpServlet#HttpServlet()
*/
public MessageProducerServlet()
{
super();
System.out.println("*** MessageProducerServlet created");
}
/**
156
157
}
}
Note that:
The closeJmsResources method is responsible for closing the JMS resources used when
sending a JMS message.
158
If using GlassFish v3, include the appropriate runtime library JAR on the classpath:
$GLASSFISH_HOME/modules/gf-client.jar
package com.ivan.scbcd6.entity;
import java.io.Serializable;
import java.util.Date;
/**
* Represents a message sent by a producer to the topic.
*/
public class MyMessage implements Serializable
{
/* Constant(s): */
private static final long serialVersionUID = -4682924711829199796L;
/* Instance variable(s): */
private String mMessageString = "";
private long mMessageNumber;
private Date mMessageTime;
public String getMessageString()
{
return mMessageString;
}
public void setMessageString(final String inMessageString)
{
mMessageString = inMessageString;
}
public long getMessageNumber()
{
return mMessageNumber;
}
public void setMessageNumber(final long inMessageNumber)
{
mMessageNumber = inMessageNumber;
}
public Date getMessageTime()
{
return mMessageTime;
}
public void setMessageTime(final Date inMessageTime)
{
mMessageTime = inMessageTime;
}
}
159
/**
*
*/
package com.ivan.scbcd6.seclient;
import java.util.Date;
import java.util.concurrent.atomic.AtomicLong;
import
import
import
import
import
import
import
import
import
javax.jms.Connection;
javax.jms.ConnectionFactory;
javax.jms.JMSException;
javax.jms.MessageProducer;
javax.jms.ObjectMessage;
javax.jms.Queue;
javax.jms.Session;
javax.naming.InitialContext;
javax.naming.NamingException;
import com.ivan.scbcd6.entity.MyMessage;
/**
* Java SE message driven bean client that sends messages to the
* JMS queue that one or more message driven beans listen to.
*
* Note that the following JAR must be included on the client's class path:
* GlassFish 3: $GLASSFISH_HOME/modules/gf-client.jar
* GlassFish 2: $GLASSFISH_HOME/lib/appserv-rt.jar and $APS_HOME/lib/javaee.jar
*
* @author Ivan A Krizsan
*/
public class MessageDrivenBeanSEClient
{
/* Constant(s): */
private final static String JMS_CONNECTIONFACTORY_JNDI =
"jms/QueueConnectionFactory";
private final static String JMS_JMS_QUEUEDESTINATION_JNDI =
"jms/QueueDestination";
/* Instance variable(s): */
/** Connection factory for queue. */
private ConnectionFactory mQueueConnectionFactory;
/** Queue destination. */
private Queue mQueueDestination;
/** MyMessage number counter. */
private AtomicLong mMessageNumber = new AtomicLong(0);
/**
* Looks up the JMS resources required by the client to send
* JMS messages.
*
* @throws NamingException If error occurs during lookup.
*/
private void lookupJmsResources() throws NamingException
{
InitialContext theContext = new InitialContext();
System.out.println("*** Starting JMS Resource Lookup...");
mQueueConnectionFactory = (ConnectionFactory) theContext
.lookup(JMS_CONNECTIONFACTORY_JNDI);
mQueueDestination = (Queue) theContext
.lookup(JMS_JMS_QUEUEDESTINATION_JNDI);
System.out.println("
}
/**
* Sends a JMS message with the next message number and increments the
* message counter.
*
* @throws JMSException If error occurred sending message.
*/
private void sendJmsMessage() throws JMSException
{
160
161
{
theException.printStackTrace();
}
System.out.println("*** Java SE JMS Client finished.");
System.exit(0);
}
}
Note that:
When using GlassFish running on the same computer as the client, no parameters are needed
when creating the InitialContext in the lookupJmsResources method.
Instead of using dependency injection, as we saw in the Java EE client example earlier, the
references to be stored in the two instance variables are looked up using JNDI in the method
lookupJmsResources.
The method sendJmsMessage is identical to the method with the same name in the Java EE
client example above.
Please refer to the Java EE example for a detailed notes on this method.
The closeJmsResources method is responsible for closing the JMS resources used when
sending a JMS message.
The method is identical to the corresponding method in the Java EE client example above.
We can conclude that the only difference between the Java EE and the Java SE clients is
resource reference retrieval; in the Java EE client references are dependency-injected while
they are looked-up in the Java SE client.
162
10.
The EJB container is responsible for implementing low-level transaction management, such as the
protocol between the transaction manager and miscellaneous resources such as databases and
messaging providers, transaction context propagation and distributed commits.
The EJB specification only require support for flat transactions, meaning that there may not be
transactions that exist within the scope of another transaction.
Please also see the chapter EJBs and Exceptions on how transactions are handled in connection to
different kinds of exceptions occurring.
163
10.1.
Transaction Propagation
A transaction context can be shared by multiple EJBs. There are three different ways that a
transaction context can propagate from an EJB that invokes the services of another EJB.
The three different ways in which transactions may propagate between EJBs.
In figure C, the transaction context in which bean A executes is suspended and bean B
executes outside of any transaction context.
For EJBs with bean managed transactions (BMT), only options B and C applies transactions from
other EJBs never propagate into an EJB with BMT.
164
Transactions can propagate between EJBs, that is a business method of bean A can call a business
method of bean B, both which may or may not execute in the same transaction. The first figure
below illustrates transaction context propagation from EJBs with container managed transactions
(CMT):
The second figure shows transaction context propagation from EJBs with bean managed
transactions (BMT):
165
10.2.
10.3.
Bean-managed transaction demarcation means that the developer uses code in the EJB to start,
commit and rollback transactions. The following is specific to EJBs with bean managed
transactions (BMT):
An EJB using BMT must not use the getRollbackOnly and setRollbackOnly method in the
EJBContext interface.
Instead the getStatus and rollback methods in UserTransaction should be used.
Only session beans and message driven beans may use bean managed transactions.
Message driven EJBs and stateless session EJBs must end a BMT before the end of the
method (business method or timeout callback method).
Stateful session EJBs may keep a bean managed transaction open over multiple business
method calls from client.
Stateless session beans and message driven beans must commit a transaction before the
method in which the transaction was started returns.
An EJB with BMT will only run in a transaction created by the EJB itself.
Client transactions never propagate into an EJB with BMT.
With BMT, the call to UserTransaction.begin() is not considered as inside the transaction
while the call to UserTransaction.commit() (or UserTransaction.rollback() ) is considered to
be inside the transaction.
If there is a need to keep the transaction open over several business method calls when using
a stateful session bean.
166
Transaction scope can be reduced to a scope smaller than a business methods, to improve
performance.
Can be used with message driven beans, in order to be able to acknowledge a message even
though the transaction rolls back.
167
Transaction management is done, as before, by invoking methods on an object that implements the
javax.transaction.UserTransaction interface, which contains the following methods:
Method Name
Description
begin
commit
getStatus
rollback
setRollbackOnly
setTransactionTimeout
168
10.3.1.
The following is an example of a stateless session bean that uses bean managed transactions. Using
transactions in this bean is quite unnecessary, as it does not use any transactional resources it is
just meant as an example of how to write bean managed transaction code in an EJB.
package com.ivan.scbcd6;
import java.util.Date;
import
import
import
import
import
import
import
import
import
import
import
import
import
javax.annotation.PostConstruct;
javax.annotation.Resource;
javax.ejb.LocalBean;
javax.ejb.SessionContext;
javax.ejb.Stateless;
javax.ejb.TransactionManagement;
javax.ejb.TransactionManagementType;
javax.transaction.HeuristicMixedException;
javax.transaction.HeuristicRollbackException;
javax.transaction.NotSupportedException;
javax.transaction.RollbackException;
javax.transaction.SystemException;
javax.transaction.UserTransaction;
/**
* Simplest possible stateless session bean exposing a local, no-interface
* view.
* The EJB has bean managed transactions.
*/
@Stateless
@LocalBean
@TransactionManagement(TransactionManagementType.BEAN)
public class StatelessSession1Bean
{
private static int sCurrentInstanceNumber = 1;
private int mInstanceNumber;
private int mCallCounter;
@Resource
private SessionContext mBeanContext;
@PostConstruct
public void initialize()
{
mInstanceNumber = sCurrentInstanceNumber++;
System.out.println("*** StatelessSession1Bean " + mInstanceNumber +
" created: " + new Date());
}
/**
* Creates a greeting to the person with the supplied name.
*
* @param inName Name of person to greet.
* @return Greeting.
*/
public String greeting(final String inName)
{
Date theCurrentTime = new Date();
String theMessage = "";
mCallCounter++;
try
{
/*
* Retrieve the UserTransaction object and start the transaction.
*/
UserTransaction theUserTransaction = mBeanContext.getUserTransaction();
System.out.println("*** Transaction begin...");
theUserTransaction.begin();
/*
* Do what is to be done inside the transaction, accessing any
* transactional resources, other EJBs etc.
*/
169
170
Note that:
The transaction is started by invoking the begin method on the UserTransaction reference.
The transaction can be rolled back by invoking rollback on the UserTransaction reference.
The transaction can be committed by invoking the commit method on the UserTransaction
reference.
There are a number of exceptions that may be thrown when using bean-managed
transactions, as can be seen in the different catch-clauses.
Especially note HeuristicMixedException and HeuristicRollbackException.
For further information on these exceptions, please consult the API documentation.
171
10.4.
Start a new transaction and consider an EJB method as an operation to be included in the
new transaction.
Container managed transactions, CMT, are declared using annotations or in the ejb-jar.xml
deployment descriptor.
A CMT transaction ends when the method that started the transaction completes.
EJB transactional behaviour can be changed without having to change the bean code.
Simplified development.
The business methods of an EJB using CMT can run in the transaction context of another
bean, which beans with BMT cannot.
172
10.4.1.
Session Synchronization
Stateful session beans with container-managed transactions can optionally implement the
SessionSynchronization interface or use one or more of the @AfterBegin, @BeforeCompletion and
@AfterCompletion annotations to receive notification when a transaction has begun, is about to end
and after it has ended.
The SessionSynchronization interface contains the following methods:
Method
Comments
void afterBegin()
void afterCompletion(boolean committed) Notifies a session bean the transaction has finished and whether
the transaction was committed or rolled back.
void beforeCompletion()
The afterBegin method gives the session bean an opportunity to load any data it needs
during a transaction.
The method executes inside a transaction, so everything is available; resource managers,
EJB environment, other EJBs, the SessionContext (except for, of course, BMT related
methods).
Corresponding annotation: @AfterBegin
The beforeCompletion method gives the session bean an opportunity to update persistent
storage prior to the transaction end.
The method also executes inside a transaction, so everything is available; resource
managers, EJB environment, other EJBs, the SessionContext (except for BMT related
methods).
The bean is also given a last chance to roll back a transaction that is about to be committed.
Corresponding annotation: @BeforeCompletion
The afterCompletion method gives the session bean an opportunity to learn the result of a
transaction and act accordingly.
This method executes outside of a transaction context, so resource managers and other EJBs
may no longer be accessed. Also, any transaction-related methods in the SessionContext
interface are now unavailable.
Corresponding annotation: @AfterCompletion
173
10.4.2.
The following is an example of a stateful session bean that uses container managed transactions as
well as implements the SessionSynchronization interface. Using transactions in this bean is quite
unnecessary, as it does not use any transactional resources it is just meant as an example of how to
write bean managed transaction code in an EJB.
package com.ivan.scbcd6;
import java.rmi.RemoteException;
import java.util.Date;
import
import
import
import
import
import
import
import
import
import
import
javax.annotation.PostConstruct;
javax.annotation.Resource;
javax.ejb.EJBException;
javax.ejb.LocalBean;
javax.ejb.SessionContext;
javax.ejb.SessionSynchronization;
javax.ejb.Stateful;
javax.ejb.TransactionAttribute;
javax.ejb.TransactionAttributeType;
javax.ejb.TransactionManagement;
javax.ejb.TransactionManagementType;
/**
* Simplest possible stateless session bean exposing a local, no-interface
* view.
* The EJB has container managed transactions.
*/
@Stateful
@LocalBean
@TransactionManagement(TransactionManagementType.CONTAINER)
public class StatefulSessionBean implements SessionSynchronization
{
private static int sCurrentInstanceNumber = 1;
private int mInstanceNumber;
private int mCallCounter;
@Resource
private SessionContext mBeanContext;
@PostConstruct
public void initialize()
{
mInstanceNumber = sCurrentInstanceNumber++;
System.out.println("*** StatelessSession1Bean " + mInstanceNumber +
" created: " + new Date());
}
/**
* Creates a greeting to the person with the supplied name.
*
* @param inName Name of person to greet.
* @return Greeting.
*/
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public String greeting(final String inName)
{
Date theCurrentTime = new Date();
String theMessage = "";
mCallCounter++;
/* Do what is to be done inside the transaction. */
theMessage = "Hello " + inName + ", I am stateless session bean " +
mInstanceNumber + ". The time is now: " + theCurrentTime;
/*
* Every third call to the same session bean instance
* the transaction will be marked for rollback.
* Marking a transaction for rollback should be done before
* throwing application exceptions that have not been
* marked as causing transaction rollback when being thrown
* using the @ApplicationException annotation or corresponding
* ejb-jar.xml deployment descriptor element.
*/
174
if (mCallCounter % 3 == 0)
{
System.out.println("*** Transaction rollback.");
mBeanContext.setRollbackOnly();
}
System.out.println("*** Transaction marked for rollback: " +
mBeanContext.getRollbackOnly());
return theMessage;
}
/**
* Notifies the stateful session bean that a new transaction
* has begun.
* This method executes in a transaction context.
*
* @throws EJBException If error occurred.
* @throws RemoteException Not used.
*/
@Override
public void afterBegin() throws EJBException, RemoteException
{
System.out.println("*** StatefulSessionBean.afterBegin");
}
/**
* Notifies the stateful session bean that a transaction has been
* completed and whether the transaction was committed or not.
* This method executes outside any transaction context.
*
* @param inCommitted True if transaction committed, false otherwise.
* @throws EJBException If error occurred.
* @throws RemoteException Not used.
*/
@Override
public void afterCompletion(boolean inCommitted) throws EJBException,
RemoteException
{
System.out.println("*** StatefulSessionBean.afterCompletion: " +
inCommitted);
}
/**
* Notifies the stateful session bean that a transaction is about
* to be completed.
* This method executes in a transaction context.
*
* @throws EJBException If error occurred.
* @throws RemoteException Not used.
*/
@Override
public void beforeCompletion() throws EJBException, RemoteException
{
System.out.println("*** StatefulSessionBean.beforeCompletion");
}
}
175
Note that:
The transaction can be marked for rollback by invoking the setRollbackOnly method on the
SessionContext reference injected into the EJB.
The EJB can inquiry about whether the transaction has been marked for rollback by calling
the getRollbackOnly method on the SessionContext reference.
10.4.3.
Transaction Attributes
176
The following table lists the available transaction attributes and their meaning:
Transaction
Attribute
Deployment
Descriptor Value
Description
MANDATORY
Mandatory
REQUIRED
Required
REQUIRES_NEW
RequiresNew
SUPPORTS
Supports
NOT_SUPPORTED NotSupported
NEVER
Never
The container will attempt to commit any transaction context created immediately prior to invoking
a method on an EJB when the method execution has completed.
A transaction attribute of a method in an EJB can be specified either using the
@TransactionAttribute annotation or the ejb-jar.xml deployment descriptor. As usual, any
configuration in the deployment descriptor overrides configuration by annotations.
177
The following table summarizes how a transaction context is propagated from a client invoking a
business method of an EJB to the business method and, finally, to resource managers used by the
business method in the case of different transaction attributes. TC is the client's transaction context,
TB is the transaction context of a transaction started immediately prior to the invocation of the
business method.
Transaction Attribute
Not Supported
Required
Supports
RequiresNew
Mandatory
Never
Client's Transaction
Business Method
Transaction
Resource Managers
Transaction
None
None
None
TC
None
None
None
TB
TB
TC
TC
TC
None
None
None
TC
TC
TC
None
TB
TB
TC
TB
TB
None
Exception
TC
TC
TC
None
None
None
TC
Exception
Transaction context container passes to business methods and resource managers from a business method.
178
The following shows an example of how to specify a transaction attribute on a method in an EJB
using annotations:
...
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public String greeting(final String inName)
{
...
The deployment descriptor can be used to specify transaction attributes for methods of EJBs or to
override transaction attributes specified using annotations.
Below is an example of different ways of specifying the method(s) for which a transaction attribute
is to be assigned.
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar version="3.1" 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_1.xsd">
...
<assembly-descriptor>
...
<!-Set all the methods in the EJB with the name StatelessSessionBean
to have the transaction attribute Required.
Note the use of the wildcard when specifying the method name(s).
There must be only one <container-transaction> element per EJB
that uses the wildcard when specifying method name.
-->
<container-transaction>
<method>
<ejb-name>StatelessSessionBean</ejb-name>
<method-name>*</method-name>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
<!-Set the method(s) named controlCitizen in the EJB with the name
StateControlSessionBean to have the transaction attribute Mandatory.
Applies to all methods with the specified name in the EJB.
-->
<container-transaction>
<method>
<ejb-name>StateControlSessionBean</ejb-name>
<method-name>controlCitizen</method-name>
</method>
<trans-attribute>Mandatory</trans-attribute>
</container-transaction>
<!-Set the method named diggForGold with the parameter-types
String, int and MyClass in the EJB with the name
LawlessSessionBean to have the transaction attribute RequiresNew.
Specifies a single method using name and parameter signature.
-->
<container-transaction>
<method>
<ejb-name>LawlessSessionBean</ejb-name>
<method-name>diggForGold</method-name>
<method-params>
<method-param>java.lang.String</method-param>
<method-param>int</method-param>
<method-param>com.ivan.scbcd6.MyClass</method-param>
</method-params>
</method>
<trans-attribute>RequiresNew</trans-attribute>
</container-transaction>
179
</assembly-descriptor>
</ejb-jar>
Note that:
In the first <container-transaction> element, the <method-name> element contains the *
character.
This is a wildcard character which matches all the methods in the EJB with the specified
name.
In the second <container-transaction> element, the name of the method(s) is explicitly
specified.
In the case where there are multiple methods with the same name in the EJB in question, the
transaction attribute will apply to all the methods.
In the third <contiainer-transaction> element, not only the method name of the method
which is to be assigned the RequiresNew transaction attribute is specified but also the
parameter signature of the method.
The ability to be able to specify parameter types enables assigning different transaction
attributes to methods with the same name.
Transaction Attributes and Inheritance
Transaction attributes of an EJB class with a superclass is determined using the following rules:
If no transaction attribute is specified for a class, then this is equal to the transaction
attribute REQUIRED having been specified on the class.
The transaction attribute defined on a class, or the default transaction attribute, applies to all
methods defined in the class.
A method defined in a superclass has the transaction attribute according to the previous
point.
A method defined in a superclass but overridden in a child class (to the superclass) has the
transaction attribute as defined in the child class, or the default transaction attribute of the
child class.
10.5.
180
Authorization
Ensure that the user only can access the resources which he has permission to access.
Authentication
Confirm the identity of the user. Commonly through the process of supplying a login and
password.
181
11.1.
As customary, we'll start this chapter with an example. The example in this chapter involves not
only developing an example application, but also security configuration of the GlassFish v3
application server. The following figure shows an overview of how the main parts related to
security are related to each other; the application server, the product-specific deployment descriptor,
the EJB client (a servlet) and finally the EJB:
Outline of the different security artifacts and their relations in this chapter's example program.
182
11.2.
In order to prepare for the security example programs, we set up the users in the GlassFish
application server. According to the instructions in appendix G, create the users listed in this table
belonging to the named groups and with the listed passwords:
User Name
11.3.
Group
Password
ivan
super-users
ivan
runas-superuser
super-users
super
johnny
plain-users
johnny
As usual, we'll develop the example program in a Dynamic Web project in Eclipse. Since a second
EJB will be introduced later, some common functionality has been extracted to a common
superclass.
Create a dynamic web project in Eclipse, as described in appendix A.
I call my project EJBSecurityAnnotations.
11.3.1.
In a later part of the example a second session bean will be introduced. The common session bean
superclass extracts common properties of the session bean implementations to minimize code
duplication.
In the package com.ivan.scbcd6.ejbs, create the common superclass for the stateless session
beans CommonStatelessSessionBean.
package com.ivan.scbcd6.ejbs;
import
import
import
import
java.security.Principal;
java.util.Date;
javax.annotation.Resource;
javax.ejb.SessionContext;
/**
* Class gathering common properties of the stateless session beans
* in this example.
*/
public class CommonStatelessSessionBean
{
/* Class variable(s): */
protected static int sCurrentInstanceNumber = 1;
/* Instance variable(s): */
@Resource
protected SessionContext mSessionContext;
protected int mInstanceNumber;
/**
* Initializes session bean instance number.
*/
public CommonStatelessSessionBean()
{
mInstanceNumber = sCurrentInstanceNumber++;
}
/**
* Prints security information from the session context, if available.
*/
183
Note that:
The class contains the mSessionContext instance variable, which is annotated with the
@Resource annotation.
Despite the instance variable being declared in the superclass of a class implementing an
EJB, dependency injection will take place.
In the method printSecurityInfo, the method getCallerPrincipal is invoked on the session
context object.
This retrieves the an object that identifies the caller of the EJB method. We'll look at more
details in the section on programmatic security.
In the method testCallerRole, the method isCallerInRole is invoked on the session context.
This method tests wether the caller is in the security role with the supplied name. Again,
further details in the section on programmatic security.
184
11.3.2.
In the first version of the security example program, there will be only one single session bean.
package com.ivan.scbcd6.ejbs;
import
import
import
import
javax.annotation.security.DeclareRoles;
javax.annotation.security.RolesAllowed;
javax.ejb.LocalBean;
javax.ejb.Stateless;
/**
* Secured stateless session bean exposing a local, no-interface view.
*/
@Stateless
@LocalBean
/*
* The roles that are to be used from within the code when invoking
* isCallerInRole need to be declared using the @DeclareRoles annotation,
* otherwise an exception will be thrown when calling isCallerInRole.
*/
@DeclareRoles({"superusers", "plainusers"})
/*
* When applied at class-level, the @RolesAllowed annotation specifies
* which security-roles are allowed to access all the methods in the
* EJB. @RolesAllowed may also be used at method-level.
*/
@RolesAllowed({"superusers", "plainusers"})
public class StatelessSession1Bean extends CommonStatelessSessionBean
{
/**
* Creates a greeting to the person with the supplied name.
*
* @param inName Name of person to greet.
* @return Greeting.
*/
public String greeting(final String inName)
{
System.out.println("*** StatelessSession1Bean.greeting");
printSecurityInfo();
String theMessage = assembleGreeting(inName, "StatelessSession1Bean");
return theMessage;
}
}
Note that:
The session bean class is annotated with the @DeclareRoles annotation in which a list of
security roles is supplied.
This annotation declares the security roles that are used for programmatic security from
within the code of the EJB. If a security role is not declared, it will be impossible to
determine if the current user is in the security role in question using the isCallerInRole
method in the SessionContext.
The code performing programmatic security is located in the superclass of the session bean.
The session bean class is annotated with the @RolesAllowed annotation in which a list of
security roles is supplied.
This annotation, which can be declared both at class- and method-level, specifies which
security roles are allowed to invoke method(s) on the EJB.
If annotating the class, then the roles applies to all methods in the EJB. If annotating a single
method, then the roles only applies to that single method.
185
11.3.3.
As usual for most of the examples in this book, the client of the EJB is a servlet.
In this example, having a servlet as client gives us the opportunity of allowing the user to log in
when running the example program.
package com.ivan.scbcd6.client;
import
import
import
import
import
import
import
import
import
import
import
java.io.IOException;
java.io.PrintWriter;
javax.ejb.EJB;
javax.servlet.ServletException;
javax.servlet.annotation.HttpMethodConstraint;
javax.servlet.annotation.ServletSecurity;
javax.servlet.annotation.WebServlet;
javax.servlet.http.HttpServlet;
javax.servlet.http.HttpServletRequest;
javax.servlet.http.HttpServletResponse;
com.ivan.scbcd6.ejbs.StatelessSession1Bean;
/**
* Servlet implementing an EJB client which calls a secured method
* of an EJB.
*/
@WebServlet(name = "EJBClientServlet", urlPatterns = "/test.do")
@ServletSecurity(
httpMethodConstraints =
{
@HttpMethodConstraint(value="GET", rolesAllowed = { "plainusers", "superusers"})
})
public class EJBClientServlet extends HttpServlet
{
/* Constant(s): */
private static final long serialVersionUID = 1L;
/* Instance variable(s): */
@EJB()
private StatelessSession1Bean mSessionBean1;
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
@Override
protected void doGet(HttpServletRequest inRequest,
HttpServletResponse inResponse) throws ServletException, IOException
{
System.out.println("*** EJBClientServlet.doGet");
PrintWriter theResponseWriter = inResponse.getWriter();
String theRequestNameParam = inRequest.getParameter("name");
if (theRequestNameParam == null)
{
theRequestNameParam = "Anonymous";
}
String theResponse = mSessionBean1.greeting(theRequestNameParam);
theResponseWriter.println("Response from the EJB:\n" + theResponse);
}
}
Despite this book not focusing on servlet technology, we should note that:
The name of the roles are not the same as the name of the user groups used when creating
users in the GlassFish application server.
186
11.3.4.
The next step consists of mapping security roles in the application to users and/or user groups in the
application server. This involves creating the product-specific web deployment descriptor for
GlassFish v3.
Note that:
For GlassFish v3, the way of mapping security roles to principals and/or user groups is the
same for the sun-web.xml (web application), sun-ejb-jar.xml (EJB) and sun-application.xml
(enterprise application) deployment descriptors.
In the first <security-role-mapping> element, the user (principal) with the name ivan that
we created earlier is mapped to the security role superusers.
This gives the user ivan access to parts of the application that requires the user to be in the
superusers role.
187
11.3.5.
After having deployed the application to GlassFish, we are now ready to give it a first try:
When asked to log in, log in using the name ivan with the password ivan.
Note that:
You were asked to log in before being able to view the webpage.
As you recall, the servlet only allows users in the roles plainusers or superusers to
invoke it.
From the console output above, we can see that the user ivan indeed is in the superusers
role.
The session bean invoked by the servlet also only allows users in the plainusers or
superusers roles to invoke it.
Servlets with security configuration will display a dialog asking the user to provide security
credentials, something an EJB with security configuration will not do.
As an exercise, remove the @ServletSecurity annotation from the servlet and re-run the
program. This will cause an exception to be thrown when the servlet tries to invoke the
session bean.
Thus we can conclude that the security principal was propagated from the servlet to the
session bean.
When the session bean is invoked, the caller is in the superusers role but not in the
plainusers role.
This of course depends on the user credentials used when logging in.
As an exercise, clear the cookies and history information in the browser and log in using the
user johnny and the password johnny and re-run the application. This user has a
different security role.
When the session bean is invoked, it cannot determine whether the caller is in a role named
ivan. Invoking the isCallerInRole method with a role name that has not been declared
using the @DeclareRoles annotation will cause an exception to be thrown.
188
11.3.6.
To look further at principal propagation, we will introduce a second session bean which will be
called from the first session bean.
package com.ivan.scbcd6.ejbs;
import
import
import
import
javax.annotation.security.DeclareRoles;
javax.annotation.security.RolesAllowed;
javax.ejb.LocalBean;
javax.ejb.Stateless;
/**
* Stateless session bean with some declarative security specified using
* annotations.
*/
@Stateless
@LocalBean
/*
* The roles that are to be used from within the code when invoking
* isCallerInRole need to be declared using the @DeclareRoles annotation,
* otherwise an exception will be thrown when calling isCallerInRole.
*/
@DeclareRoles({"superusers", "plainusers"})
/*
* When applied at class-level, the @RolesAllowed annotation specifies
* which security-roles are allowed to access all the methods in the
* EJB. @RolesAllowed may also be used at method-level.
*/
@RolesAllowed({"superusers", "plainusers", "runasadmin"})
public class StatelessSession2Bean extends CommonStatelessSessionBean
{
/**
* Creates a greeting to the person with the supplied name.
*
* @param inName Name of person to greet.
* @return Greeting.
*/
public String greeting(final String inName)
{
System.out.println("*** StatelessSession2Bean.greeting");
printSecurityInfo();
try
{
Thread.sleep(2000L);
} catch (InterruptedException theException)
{
/* Ignore exceptions. */
}
return assembleGreeting(inName, "StatelessSession2Bean");
}
/**
* Returns a string.
*
* @return String.
*/
@RolesAllowed("superusers")
public String superusersOnly()
{
System.out.println("*** StatelessSession2Bean.superusersOnly");
printSecurityInfo();
return "Bingo!";
}
}
189
Note that:
As with the first session bean, this class is also annotated with the @DeclareRoles
annotation in which a list of security roles used with programmatic security is specified.
The code performing programmatic security is located in the superclass of the session bean.
This session bean class is also annotated with the @RolesAllowed annotation in which a list
of security roles is supplied.
This annotation specifies the default security restrictions for all the methods of the session
bean.
The method superusersOnly is also annotated with the @RolesAllowed annotation,
specifying only the superusers role.
This annotation overrides the security restrictions that the @RolesAllowed annotation
specified for all the methods of the class and only allows users belonging to the superusers
security group to access the method.
We also need to modify the first session bean, so that invokes the second session bean:
Modify the implementation of the StatelessSession1Bean class to look like this:
package com.ivan.scbcd6.ejbs;
import
import
import
import
import
javax.annotation.security.DeclareRoles;
javax.annotation.security.RolesAllowed;
javax.ejb.EJB;
javax.ejb.LocalBean;
javax.ejb.Stateless;
/**
* Secured stateless session bean exposing a local, no-interface view.
*/
@Stateless
@LocalBean
/*
* The roles that are to be used from within the code when invoking
* isCallerInRole need to be declared using the @DeclareRoles annotation,
* otherwise an exception will be thrown when calling isCallerInRole.
*/
@DeclareRoles(
{ "superusers", "plainusers" })
/*
* When applied at class-level, the @RolesAllowed annotation specifies
* which security-roles are allowed to access all the methods in the
* EJB. @RolesAllowed may also be used at method-level.
*/
@RolesAllowed({ "superusers", "plainusers" })
public class StatelessSession1Bean extends CommonStatelessSessionBean
{
@EJB
private StatelessSession2Bean mSessionBean2;
/**
* Creates a greeting to the person with the supplied name.
*
* @param inName Name of person to greet.
* @return Greeting.
*/
public String greeting(final String inName)
{
System.out.println("*** StatelessSession1Bean.greeting");
printSecurityInfo();
try
{
System.out.println("
Message for the superuser: " +
mSessionBean2.superusersOnly());
} catch (Throwable theException)
{
System.out.println("
No message for the superuser.");
}
String theBean2Greeting = mSessionBean2.greeting(inName);
190
Note that:
The class now contains the instance field mSessionBean2, annotated with the @EJB
annotation.
This field holds a reference to the second session bean.
The greeting method tries to invoke the superusersOnly method on the second session bean.
A certain message will be printed if the invocation succeeds, another will be printed if the
invocation fails.
The greeting method also invokes the greeting method on the second session bean and
appends the result to the message it produces.
11.3.7.
We are now ready to try the new version of the EJB security example program:
Clear cookies and cached data in the browser used to access the example program.
When asked to log in, log in using the name johnny with the password johnny.
In the console, you should see the following (exception stacktrace edited to conserve space):
INFO: *** EJBClientServlet.doGet
INFO: *** StatelessSession1Bean.greeting
INFO:
Principal name: johnny
INFO:
Principal object: johnny
INFO:
Principal type: class org.glassfish.security.common.PrincipalImpl
INFO:
Caller in 'superusers' role? false
INFO:
Caller in 'plainusers' role? true
INFO:
Cannot determine caller role: 'ivan'
INFO: JACC Policy Provider: Failed Permission Check,
context(EJBSecurityAnnotations/EJBSecurityAnnotations_internal)permission((javax.security.jacc.EJBMethodPermission StatelessSession2Bean
superusersOnly,Local,))
WARNING: A system exception occurred during an invocation on EJB StatelessSession2Bean
method public java.lang.String
com.ivan.scbcd6.ejbs.StatelessSession2Bean.superusersOnly()
javax.ejb.AccessLocalException: Client not authorized for this invocation.
at com.sun.ejb.containers.BaseContainer.preInvoke(BaseContainer.java:1850)
at
com.sun.ejb.containers.EJBLocalObjectInvocationHandler.invoke(EJBLocalObjectInvocationHa
ndler.java:188)
at
com.sun.ejb.containers.EJBLocalObjectInvocationHandlerDelegate.invoke(EJBLocalObjectInvo
cationHandlerDelegate.java:84)
at $Proxy220.superusersOnly(Unknown Source)
at
com.ivan.scbcd6.ejbs.__EJB31_Generated__StatelessSession2Bean__Intf____Bean__.superusers
Only(Unknown Source)
191
at
com.ivan.scbcd6.ejbs.StatelessSession1Bean.greeting(StatelessSession1Bean.java:47)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at
org.glassfish.ejb.security.application.EJBSecurityManager.runMethod(EJBSecurityManager.j
ava:1056)
at
org.glassfish.ejb.security.application.EJBSecurityManager.invoke(EJBSecurityManager.java
:1128)
at com.sun.ejb.containers.BaseContainer.invokeBeanMethod(BaseContainer.java:5292)
at com.sun.ejb.EjbInvocation.invokeBeanMethod(EjbInvocation.java:615)
at
com.sun.ejb.containers.interceptors.AroundInvokeChainImpl.invokeNext(InterceptorManager.
java:797)
at com.sun.ejb.EjbInvocation.proceed(EjbInvocation.java:567)
at
com.sun.ejb.containers.interceptors.SystemInterceptorProxy.doAround(SystemInterceptorPro
xy.java:157)
at
com.sun.ejb.containers.interceptors.SystemInterceptorProxy.aroundInvoke(SystemIntercepto
rProxy.java:139)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at
com.sun.ejb.containers.interceptors.AroundInvokeInterceptor.intercept(InterceptorManager
.java:858)
at
com.sun.ejb.containers.interceptors.AroundInvokeChainImpl.invokeNext(InterceptorManager.
java:797)
at
com.sun.ejb.containers.interceptors.InterceptorManager.intercept(InterceptorManager.java
:367)
at com.sun.ejb.containers.BaseContainer.__intercept(BaseContainer.java:5264)
at com.sun.ejb.containers.BaseContainer.intercept(BaseContainer.java:5252)
at
com.sun.ejb.containers.EJBLocalObjectInvocationHandler.invoke(EJBLocalObjectInvocationHa
ndler.java:190)
at
com.sun.ejb.containers.EJBLocalObjectInvocationHandlerDelegate.invoke(EJBLocalObjectInvo
cationHandlerDelegate.java:84)
at $Proxy221.greeting(Unknown Source)
at
com.ivan.scbcd6.ejbs.__EJB31_Generated__StatelessSession1Bean__Intf____Bean__.greeting(U
nknown Source)
at com.ivan.scbcd6.client.EJBClientServlet.doGet(EJBClientServlet.java:54)
...
INFO:
No message for the superuser.
INFO: *** StatelessSession2Bean.greeting
INFO:
Principal name: johnny
INFO:
Principal object: johnny
INFO:
Principal type: class org.glassfish.security.common.PrincipalImpl
INFO:
Caller in 'superusers' role? false
INFO:
Caller in 'plainusers' role? true
INFO:
Cannot determine caller role: 'ivan'
192
In addition to what we saw when running the first version of the example program, note that:
We can see that the user johnny is in the plainusers role and not in the superusers role.
The first session bean, StatelessSession1Bean, did not succeed in invoking the
superusersOnly method on the second session bean, StatelessSession2Bean.
The superusersOnly method may only be invoked by users in the superusers role, but
johnny is in the plainusers role and the invocation thus fails.
The resulting exception was caught and a stacktrace was printed to the console.
Curiously, we can see that the container uses interceptors to apply security restricitions to
EJBs (indications highlighted with green in the stacktrace).
The first session bean succeeds in invoking the greeting method on the second session bean.
This since users in the plainusers role are allowed to invoke this method.
The messages from both the session beans are output in the browser.
11.3.8.
In this section we will modify the example program so that the second session bean is invoked
using a fixed security role, which may or may not be the same as the original security role. More
details about how to change the security role an EJB uses when invoking other EJBs can be found
in the section on Declarative Security later in this chapter.
Add the @RunAs annotation to the StatelessSession1Bean class, as shown in the listing
below.
...
@RolesAllowed({ "superusers", "plainusers" })
@RunAs("runasadmin")
public class StatelessSession1Bean extends CommonStatelessSessionBean
{
...
Clear cookies and cached data in the browser used to access the example program.
When asked to log in, log in using the name johnny with the password johnny.
The message in the browser should be similar to what we have seen earlier.
In the console, you should see the following (exception stacktrace edited to conserve space):
193
com.ivan.scbcd6.ejbs.StatelessSession2Bean.superusersOnly()
javax.ejb.AccessLocalException: Client not authorized for this invocation.
at com.sun.ejb.containers.BaseContainer.preInvoke(BaseContainer.java:1850)
...
INFO:
No message for the superuser.
INFO: *** StatelessSession2Bean.greeting
INFO:
Principal name: runas-superuser
INFO:
Principal object: runas-superuser
INFO:
Principal type: class org.glassfish.security.common.PrincipalImpl
INFO:
Caller in 'superusers' role? false
INFO:
Caller in 'plainusers' role? false
INFO:
Cannot determine caller role: 'ivan'
Note that:
When executing in the StatelessSession1Bean, the name of the principal is johnny and the
caller is in the security role plainusers.
The first session bean, StatelessSession1Bean, did not succeed in invoking the
superusersOnly method on the second session bean, StatelessSession2Bean.
This is not entirely surprising, as the caller is in the role plainusers when executing in the
first session bean.
When executing in the StatelessSession2Bean, the name of the principal has changed to
runas-superuser and the caller is neither in the security role superusers nor in the role
plainusers.
Apply one of the above suggested ways to enable the runas-superuser principal to execute
the superuserOnly method.
Deploy the new version of the example program to the GlassFish server.
Clear cookies and cached data in the browser used to access the example program.
When asked to log in, log in using the name johnny with the password johnny.
The message in the browser should be similar to what we have seen earlier.
194
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
Note that:
Again, the principal name when executing in the StatelessSession1Bean is johnny and the
user is in the security role plainusers.
When executing in the superuserOnly method, the principal name is runas-superuser and
the user is still not in any of the roles superusers or plainusers.
This will give you a hint about the solution I used to allow execution of the superusersOnly
method, which was by adding the role runasadmin to the @RolesAllowed annotation on
the method.
This concludes the EJB security example that uses annotations. In the next section we will look at
how to modify the example program and replace all annotations with configuration in the ejbjar.xml deployment descriptor.
195
11.4.
When creating the example program that only uses the ejb-jar.xml deployment descriptor for
configuration of the EJBs, you can either choose to modify the example program above or create a
new one. I choose to create a new example program. If you modify the existing project, then skip
the two first steps below.
Copy all the classes and the sun-web.xml deployment descriptor from the previous example
program to the new project.
196
<role-link>superusers</role-link>
</security-role-ref>
<security-role-ref>
<role-name>plainusers</role-name>
<role-link>plainusers</role-link>
</security-role-ref>
</session>
<session>
<ejb-name>StatelessSession1Bean</ejb-name>
<local-bean/>
<ejb-class>com.ivan.scbcd6.ejbs.StatelessSession1Bean</ejb-class>
<session-type>Stateless</session-type>
<!-Inject a reference to the second session bean.
-->
<ejb-local-ref>
<ejb-ref-name>StatelessSession2Bean</ejb-ref-name>
<ejb-ref-type>Session</ejb-ref-type>
<injection-target>
<injection-target-class>
com.ivan.scbcd6.ejbs.StatelessSession1Bean
</injection-target-class>
<injection-target-name>mSessionBean2</injection-target-name>
</injection-target>
</ejb-local-ref>
<!-Inject the session context.
-->
<resource-env-ref>
<resource-env-ref-name>java:comp/EJBContext</resource-env-ref-name>
<resource-env-ref-type>javax.ejb.EJBContext</resource-env-ref-type>
<injection-target>
<injection-target-class>
com.ivan.scbcd6.ejbs.CommonStatelessSessionBean
</injection-target-class>
<injection-target-name>mSessionContext</injection-target-name>
</injection-target>
</resource-env-ref>
<security-role-ref>
<role-name>superusers</role-name>
<role-link>superusers</role-link>
</security-role-ref>
<security-role-ref>
<role-name>plainusers</role-name>
<role-link>plainusers</role-link>
</security-role-ref>
<security-role-ref>
<role-name>runasadmin</role-name>
<role-link>runasadmin</role-link>
</security-role-ref>
<!-Specifies the security identity the EJB will use when
calling other EJBs and Java EE components.
Corresponds to the @RunAs annotation.
-->
<security-identity>
<run-as>
<role-name>runasadmin</role-name>
</run-as>
</security-identity>
</session>
</enterprise-beans>
<assembly-descriptor>
<!-Defines the security roles used in the EJB module.
These roles are mapped to application server principals or
user groups in the application server specific deployment
descriptor - for GlassFish this is sun-ejb-jar.xml.
-->
<security-role>
<role-name>superusers</role-name>
197
</security-role>
<security-role>
<role-name>plainusers</role-name>
</security-role>
<security-role>
<role-name>runasadmin</role-name>
</security-role>
<!-Allow access to all of the methods in the StatelessSession1Bean
for users in the superusers and plainusers security roles.
-->
<method-permission>
<role-name>superusers</role-name>
<role-name>plainusers</role-name>
<method>
<ejb-name>StatelessSession1Bean</ejb-name>
<method-name>*</method-name>
</method>
</method-permission>
<!-Allow access to the greeting method in the StatelessSession2Bean
for users in the superusers, plainusers and runasadmin
security roles.
-->
<method-permission>
<role-name>superusers</role-name>
<role-name>plainusers</role-name>
<role-name>runasadmin</role-name>
<method>
<ejb-name>StatelessSession2Bean</ejb-name>
<method-name>greeting</method-name>
</method>
</method-permission>
<!-Allow access to the superusersOnly method in the
StatelessSession2Bean for users in the superusers and
runasadmin security roles.
-->
<method-permission>
<role-name>superusers</role-name>
<role-name>runasadmin</role-name>
<method>
<ejb-name>StatelessSession2Bean</ejb-name>
<method-name>superusersOnly</method-name>
</method>
</method-permission>
</assembly-descriptor>
</ejb-jar>
Note that:
There are two EJBs defined in the deployment descriptor; StatelessSession1Bean and
StatelessSession2Bean.
As the names hints, both these EJBs are stateless session beans. Both EJBs have a local, nointerface, view.
An EJB context is injected in both the session beans using a <resource-env-ref> element.
Also note that the injection target class is the common session bean superclass, since the
instance field is declared in this class.
A reference to the StatelessSession2Bean is injected into the StatelessSession1Bean using a
<ejb-local-ref> element.
In each of the session beans, there are a number of <security-role-ref> elements.
Such an element declares a mapping between a security role name used in the code of the
EJB in which the element is declared (in the <role-name> element) and a security role name
in the application (in the <role-link> element). The former security role name can only be
used within the EJB in which it is declared. The latter security role name is global to the
198
application.
These mappings are only required for programmatic security.
In the StatelessSession1Bean, there is a <security-identity> element.
This element contains a <run-as> element, which in turn contains a <role-name> element.
This construction specifies the security role that will be used by all methods of the EJB in
which it appears when calling other EJBs and Java EE components.
This corresponds to the @RunAs annotation.
In the <assembly-descriptor> element, there are a number of <security-role> elements.
Each of these elements declare a security role that can be used in the application.
These security roles must be mapped to users (principals) and/or user groups in a serverspecific deployment descriptor. In our case, these mappings are done in the sun-web.xml
deployment descriptor, as described earlier.
In the <assembly-descriptor> element, there are three <method-permission> elements.
Each of these elements declare the security role(s) that are allowed to execute certain
method(s) on an EJB.
The first <method-permission> element allows the security roles superusers and
plainusers to execute all methods in the StatelessSession1Bean.
The second <method-permission> element allows the security roles superusers,
plainusers and runasadmin to execute the greeting method in the StatelessSession2Bean.
Finally, the third <method-permission> element allows the security roles superusers and
plainusers to execute the superuserOnly method in the StatelessSession2Bean.
With the ejb-jar.xml deployment descriptor in place, we are done and can try the example out.
Clear cookies and cached data in the browser used to access the example program.
When asked to log in, log in using the name johnny with the password johnny.
199
*** EJBClientServlet.doGet
*** StatelessSession1Bean.greeting
Principal name: johnny
Principal object: johnny
Principal type: class org.glassfish.security.common.PrincipalImpl
Caller in 'superusers' role? false
Caller in 'plainusers' role? true
Cannot determine caller role: 'ivan'
*** StatelessSession2Bean.superusersOnly
Principal name: runas-superuser
Principal object: runas-superuser
Principal type: class org.glassfish.security.common.PrincipalImpl
Caller in 'superusers' role? false
Caller in 'plainusers' role? false
Cannot determine caller role: 'ivan'
Message for the superuser: Bingo!
*** StatelessSession2Bean.greeting
Principal name: runas-superuser
Principal object: runas-superuser
Principal type: class org.glassfish.security.common.PrincipalImpl
Caller in 'superusers' role? false
Caller in 'plainusers' role? false
Cannot determine caller role: 'ivan'
Not surprisingly, this is the same result as we saw when using annotations to configure EJB
security. In the rest of this chapter we will look at the different options available in connection to
EJB security.
200
11.5.
Programmatic Security
Using programmatic security in EJBs is discouraged and has the following drawbacks:
Mixes security infrastructure and business logic code in the EJB class.
If programmatic security must be used, a better alternative is place programmatic security checks in
an interceptor and thus separating it from the business logic code.
There are two security-related methods in the EJBContext interface that are not deprecated:
Method Name
Description
getCallerPrincipal()
isCallerInRole(String)
The following method from the security examples in the beginning of this chapter shows use of the
above security-related methods in the EJBContext interface:
...
protected void printSecurityInfo()
{
if (mSessionContext != null)
{
/*
* Container may never return a null principal, so we don't
* need to make sure it is not null.
*/
Principal theCallerPrincipal = mSessionContext.getCallerPrincipal();
System.out.println("
Principal name: " +
theCallerPrincipal.getName());
System.out.println("
Principal object: " + theCallerPrincipal);
System.out.println("
Principal type: " + theCallerPrincipal.getClass());
testCallerRole("superusers");
testCallerRole("plainusers");
testCallerRole("ivan");
} else
{
System.out.println("
No session context available.");
}
}
private void testCallerRole(String inRoleName)
{
try
{
System.out.println("
Caller in '" + inRoleName + "' role? " +
mSessionContext.isCallerInRole(inRoleName));
} catch (Throwable theException)
{
/* Catch exceptions thrown, for instance if role has not been declared. */
System.out.println("
Cannot determine caller role: '" +
inRoleName + "'");
}
201
}
...
Note that an EJBContext is not available at all occasions in the life-cycle of an EJB.
Programmatic security in EJBs is not purely programmatic, as we have seen. Names of roles used
when calling isCallerInRole must be declared either using the @DeclareRoles annotation or the
<security-role-ref> element in the ejb-jar.xml deployment descriptor.
The following code snippet shows how security role references are declared using the
@DeclareRoles annotation:
...
@Stateless
@LocalBean
/*
* The roles that are to be used from within the code when invoking
* isCallerInRole need to be declared using the @DeclareRoles annotation,
* otherwise an exception will be thrown when calling isCallerInRole.
*/
@DeclareRoles({ "superusers", "plainusers" })
/*
* When applied at class-level, the @RolesAllowed annotation specifies
* which security-roles are allowed to access all the methods in the
* EJB. @RolesAllowed may also be used at method-level.
*/
@RolesAllowed({ "superusers", "plainusers" })
@RunAs("runasadmin")
public class StatelessSession1Bean extends CommonStatelessSessionBean
...
The following excerpt shows how the same roles declared using the annotation in the example
above may be declared in the ejb-jar.xml deployment descriptor using two <security-role-ref>
elements:
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar version="3.1" 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_1.xsd">
<enterprise-beans>
<session>
<ejb-name>StatelessSession2Bean</ejb-name>
<local-bean/>
<ejb-class>com.ivan.scbcd6.ejbs.StatelessSession2Bean</ejb-class>
<session-type>Stateless</session-type>
<resource-env-ref>
<resource-env-ref-name>java:comp/EJBContext</resource-env-ref-name>
<resource-env-ref-type>javax.ejb.EJBContext</resource-env-ref-type>
<injection-target>
<injection-target-class>
com.ivan.scbcd6.ejbs.CommonStatelessSessionBean
</injection-target-class>
<injection-target-name>mSessionContext</injection-target-name>
</injection-target>
</resource-env-ref>
<!-Defines a security role used in the code of the EJB, for
instance when invoking isCallerInRole, and maps the
role name to a security role defined in the
<assembly-descriptor>.
The security role mapping is local to the component it
is defined in, in this case the StatelessSession2Bean EJB.
-->
<security-role-ref>
<!-The <role-name> element specifies the rolename
used for programmatic security, that is, calling
202
isCallerInRole.
-->
<role-name>superusers</role-name>
<!-Links the above role name to a security role defined
in a <security-role> element in the <assembly-descriptor>
below.
-->
<role-link>superusers</role-link>
</security-role-ref>
<security-role-ref>
<role-name>plainusers</role-name>
<role-link>plainusers</role-link>
</security-role-ref>
</session>
...
Note that:
In addition to the role name, it is also possible to declare a role link in a <security-role-ref>
element.
The <role-name> element in the <security-role-ref> element specifies the role-name that
may be used in the code of the EJB, when calling isCallerInRole.
This security role name is component-local and not available outside of the EJB in which
the <security-role-ref> element is declared.
The <role-link> element links the component-local security role name specified in the <rolename> element to an application global security role name declared in a <security-role>
element in the <assembly-descriptor> element.
Please refer to the example programs in the beginning of this chapter for the complete code and
deployment descriptor!
203
11.6.
Declarative Security
Declarative security is the preferred way of configuring security for EJBs. Security configuration
can be accomplished using either annotations or the ejb-jar.xml deployment descriptor. We have
already seen examples of declarative security using annotations and the deployment descriptor. In
this section we will look more closely into the options available.
11.6.1.
The security-related annotations used with EJBs are all common Java EE security annotations. The
comments below applies foremost to EJBs.
Annotation
Comments
DeclareRoles
DenyAll
PermitAll
RolesAllowed
RunAs
Declared on class-level to specify the security role the EJB is to use when
invoking other Java EE components.
The following rules apply regarding inheritance of security roles declared using the
@RolesAllowed annotation:
A method defined in a subclass does not inherit security roles from any @RolesAllowed
annotation defined on the superclass or a method in the superclass.
204
The methods in the StatelessSession1Bean can be invoked by users in the following roles:
205
11.6.2.
As with earlier versions of EJB and as we have seen in one of the examples opening this chapter,
EJB security can be configured in deployment descriptors, without use of annotations.
The following figure shows the relationships between an EJB implementation class using
programmatic and declarative security, the ejb-jar.xml deployment descriptor and the application
server-specific deployment descriptor:
An EJB using programmatic and declarative security and its security-related artifacts.
There are two sections in the ejb-jar.xml deployment descriptor that are related to security; one is
component-local and the other is EJB-module global.
206
The component-local security section declares component-local security role reference declarations
and security-identity declarations. The following XML schema fragment shows the parts of the ejbjar.xml deployment descriptor containing component-local security declarations:
Both <security-role-ref> elements and <security-identity> elements are immediate child elements of
elements containing EJB declarations, for instance <session> for session beans.
A <security-role-ref> element contains declaration of a security role name used with programmatic
security when invoking EJBContext.isCallerInRole. The <role-name> element specifies the role
name used as parameter to isCallerInRole method. The <role-link> element links the componentlocal role name specified in the <role-name> element to an EJB-module global role name defined in
the <assembly-descriptor>. If the name of the security role in the <role-name> element is the same
as the EJB-module global security role, then the <role-link> element need not be defined.
A <security-identity> element contains a specification of which security role is to be used when the
EJB invokes other EJBs and Java EE components. If the <use-caller-identity> element is used, then
the caller's security role will be passed on. If the <run-as> element is used, the security role with the
name specified in the <role-name> element in the <run-as> will be used.
207
The EJB-module security section defines security roles used in the EJB module. The following
XML schema fragment shows the parts of the ejb-jar.xml deployment descriptor containing EJB
module security declarations:
Elements of the ejb-jar.xml deployment descriptor containing EJB-module global security declarations.
208
Security Roles
One or more <security-role> elements in the <assembly-descriptor> element defines the (logical)
security roles used in the EJB module. These security roles are later mapped to principals and/or
user groups in the application-server specific deployment descriptor, as we saw an example of in the
first security example program above.
Method Permissions
One or more <method-permission> elements assigns one or more security roles to one or more
methods of an EJB in the EJB module. A user in any of the security roles assigned to a method is
allowed to invoke the method. The following are examples of how to specify method permissions in
the ejb-jar.xml deployment descriptor:
...
<!-Allow access to all of the methods in the StatelessSession1Bean
for users in the superusers and plainusers security roles.
-->
<method-permission>
<role-name>superusers</role-name>
<role-name>plainusers</role-name>
<method>
<ejb-name>StatelessSession1Bean</ejb-name>
<method-name>*</method-name>
</method>
</method-permission>
...
...
<!-Allow access to the greeting method in the StatelessSession2Bean
for users in the superusers, plainusers and runasadmin
security roles.
-->
<method-permission>
<role-name>superusers</role-name>
<role-name>plainusers</role-name>
<role-name>runasadmin</role-name>
<method>
<ejb-name>StatelessSession2Bean</ejb-name>
<method-name>greeting</method-name>
</method>
</method-permission>
...
209
...
<!-Allow access to the greeting method that takes a String parameter
in the StatelessSession2Bean for users in the superusers, plainusers
and runasadmin security roles.
-->
<method-permission>
<role-name>superusers</role-name>
<role-name>plainusers</role-name>
<role-name>runasadmin</role-name>
<method>
<ejb-name>StatelessSession2Bean</ejb-name>
<method-name>greeting</method-name>
<method-params>
<method-param>java.lang.String</method-param>
</method-params>
</method>
</method-permission>
...
Specify the EJB interface in which the method with the specified name appears.
The following interface types may be used:
Home, Remote, LocalHome, Local, ServiceEndpoint, Timer, MessageEndpoint
In addition, method parameter types may also be specified.
Example:
...
<!-Allow access to the greeting method that takes a String parameter
in the local view of the StatelessSession2Bean
for users in the superusers, plainusers and runasadmin
security roles.
-->
<method-permission>
<role-name>superusers</role-name>
<role-name>plainusers</role-name>
<role-name>runasadmin</role-name>
<method>
<ejb-name>StatelessSession2Bean</ejb-name>
<method-intf>Local</method-intf>
<method-name>greeting</method-name>
<method-params>
<method-param>java.lang.String</method-param>
</method-params>
</method>
</method-permission>
...
210
Exclude List
The exclude list may be used by an application assembler to mark methods as not being available,
regardless of which security role they are invoked with.
Example:
...
<assembly-descriptor>
....
<exclude-list>
<description>Not allowed methods</description>
<method>
<description>First method not allowed</description>
<!-Name of EJB in which method to be excluded is.
Mandatory.
-->
<ejb-name>StatelessSession2Bean</ejb-name>
<!-If the method we want to exclude occurs in several
interfaces of the EJB, then here we can specify
the interface in which the method to exclude occurs.
Optional.
Possible interfaces are:
Home
- Remote home interface.
Remote
- Remote component and business interfaces.
LocalHome
- Local home interface.
Local
- Local component and business interfaces,
no-interface view.
ServiceEndpoint - Web service interface.
Timer
- Timeout callback methods.
MessageEndpoint - Message-listener method(s) of a
message listener bean.
-->
<method-intf>Local</method-intf>
<!-Name of method to be excluded. Required.
-->
<method-name>greeting</method-name>
<!-If there are multiple methods with the same name,
the method parameter types can be specified
in this element. Optional.
-->
<method-params>
<method-param>java.lang.String</method-param>
</method-params>
</method>
</exclude-list>
</assembly-descriptor>
...
The example above adds a method with the following properties to the exclude list:
Method is a method of the StatelessSession2Bean EJB.
Method is in the local interfaces.
The local interfaces are the local component and business interfaces.
Specifying the interface is optional and only required if methods with the same name appear
in multiple interfaces of the EJB.
Name of the method to be excluded is greeting.
Takes a String object as parameter.
Specifying method parameters is also optional and only necessary if there is a need to
distinguish between multiple methods with the same name.
211
12.
12.1.
As usual, we'll start with an example showing some of the features available in EJB 3.1 for
scheduling execution of business logic.
12.1.1.
First we'll create the Eclipse project in which we'll develop the two EJBs in the example:
12.1.2.
The first EJB, a stateless session bean, that we'll develop shows how to use the @Schedule
annotation to automatically create and schedule a timer that invokes the annotated timeout callback
method.
package com.ivan.scbcd6;
import
import
import
import
import
import
import
import
import
java.util.Date;
javax.annotation.PostConstruct;
javax.annotation.Resource;
javax.ejb.EJBException;
javax.ejb.NoSuchObjectLocalException;
javax.ejb.Schedule;
javax.ejb.SessionContext;
javax.ejb.Stateless;
javax.ejb.Timer;
/**
* Stateless session bean that contains two timer callback methods that
* are scheduled using the @Schedule annotation.
* An EJB may contain any number of methods annotated with @Schedule
* that all are invoked according to their own schedule.
*/
@Stateless
public class ScheduledStatelessSessionBean
{
/* Constant(s): */
/* Class variable(s): */
private static int sCurrentInstanceNo = 1;
/* Instance variable(s): */
private int mInvocationCounter;
private int mInstanceNo = sCurrentInstanceNo++;
212
@Resource
private SessionContext mBeanContext;
@PostConstruct
public void initialize()
{
/* Log the session bean instance creation. */
System.out.println("ScheduledStatelessSessionBean created: "
+ mInstanceNo + " at: " + new Date());
}
/**
* Scheduled method invoked every 20th and 45th second every minute
* between 6 o'clock in the morning and 22 o'clock in the evening.
*
* @param inTimer Timer that caused the timeout callback invocation.
*/
@SuppressWarnings("unused")
@Schedule(second = "20, 45", minute = "*", hour = "6-22",
dayOfWeek = "Mon-Fri", dayOfMonth = "*", month = "*", year = "*",
info = "MyTimer")
private void scheduledMethod1(final Timer inTimer)
{
System.out.println("ScheduledStatelessSessionBean.scheduledMethod1: "
+ mInstanceNo + " entering at: " + new Date());
System.out.println("
Rollback only: " + mBeanContext.getRollbackOnly());
System.out.println("
Timer info: " + inTimer.getInfo());
/*
* Wait some time to show what happens with multiple timer
* callback methods being invoked on a session bean that
* has container managed concurrency.
*/
waitSeconds(15);
cancelOverdue(inTimer);
System.out.println("ScheduledStatelessSessionBean.scheduledMethod1: "
+ mInstanceNo + " exiting at: " + new Date());
}
/**
* Scheduled method invoked every 10th second within the minute
* starting at 15th second.
* Timeout callback methods need not take a Timer object, as is the
* case with this method.
*/
@SuppressWarnings("unused")
@Schedule(second = "15/10", minute = "*", hour = "*")
private void scheduledMethod2()
{
System.out.println("ScheduledStatelessSessionBean.scheduledMethod2: "
+ mInstanceNo + " entering at: "
+ new Date());
/*
* Wait some time to show what happens with multiple timer
* callback methods being invoked on a session bean that
* has container managed concurrency.
*/
waitSeconds(2);
System.out.println("ScheduledStatelessSessionBean.scheduledMethod2: "
+ mInstanceNo + " exiting at: " + new Date());
}
private void cancelOverdue(final Timer inTimer)
throws IllegalStateException, NoSuchObjectLocalException, EJBException
{
/* Cancel timer after certain number of invocations. */
if (mInvocationCounter++ > 5)
{
System.out.println("Cancelling " + mInstanceNo + "...");
inTimer.cancel();
}
}
213
Note that:
The session bean context is injected into the mBeanContext instance variable.
There is a method named scheduledMethod1 that takes a parameter of the type Timer.
This method is a timeout callback method.
The cancelOverdue method cancels a timer if a counter has reached a certain number.
214
12.1.3.
Having implemented the stateless session bean in the previous section we are now ready to try the
example program out for the first time. There is no need for a client to the EJB since, when it is
deployed, the timeout callback methods will be scheduled for invocation and later invoked by the
container.
After some time, you should see output in the GlassFish log similar to this:
...
INFO: Portable JNDI names for EJB ScheduledStatelessSessionBean :
[java:global/TimerExample/ScheduledStatelessSessionBean!
com.ivan.scbcd6.ScheduledStatelessSessionBean,
java:global/TimerExample/ScheduledStatelessSessionBean]
INFO: TimerExample was successfully deployed in 205 milliseconds.
INFO: ScheduledStatelessSessionBean created: 2 at: Wed Dec 08 07:12:15 CET 2010
INFO: ScheduledStatelessSessionBean.scheduledMethod2: 2 entering at: Wed Dec 08 07:12:15
CET 2010
INFO: ScheduledStatelessSessionBean.scheduledMethod2: 2 exiting at: Wed Dec 08 07:12:17
CET 2010
INFO: ScheduledStatelessSessionBean.scheduledMethod1: 2 entering at: Wed Dec 08 07:12:20
CET 2010
INFO:
Rollback only: false
INFO:
Timer info: MyTimer
INFO: ScheduledStatelessSessionBean created: 3 at: Wed Dec 08 07:12:25 CET 2010
INFO: ScheduledStatelessSessionBean.scheduledMethod2: 3 entering at: Wed Dec 08 07:12:25
CET 2010
INFO: ScheduledStatelessSessionBean.scheduledMethod2: 3 exiting at: Wed Dec 08 07:12:27
CET 2010
...
Note that:
The method scheduledMethod2 is invoked and finishes executing on the EJB instance with
number 2.
The method scheduledMethod1 is invoked and starts executing on the EJB instance with
number 2.
The method scheduledMethod2 is invoked, and finishes executing, on the EJB instance with
number 3.
The timer info output to the console is the string specified in the @Schedule annotation.
We can thus conclude that scheduling collisions are resolved by GlassFish by creating a new
instance of the stateless session bean and invoking the timeout callback method to be
executed next on the new instance.
Note that this is the behaviour displayed by the version of GlassFish I am using and not
behaviour stipulated by the EJB 3.1 specification.
215
12.1.4.
Adding to our scheduling example project, we will create a singleton session bean that, when the
application starts up, programmatically schedules a recurring timer. We will also look at the use of
transactions in timeout callback methods.
package com.ivan.scbcd6;
import java.util.Date;
import
import
import
import
import
import
import
import
import
import
import
import
javax.annotation.PostConstruct;
javax.annotation.Resource;
javax.ejb.LocalBean;
javax.ejb.SessionContext;
javax.ejb.Singleton;
javax.ejb.Startup;
javax.ejb.TimedObject;
javax.ejb.Timer;
javax.ejb.TimerConfig;
javax.ejb.TimerService;
javax.ejb.TransactionAttribute;
javax.ejb.TransactionAttributeType;
/**
* Singleton session bean that programmatically starts a timer
* at the startup of the application in which the EJB is deployed.
* Note that the class implements the TimedObject interface, which
* could have been replaced by using the @Timeout annotation.
*
* The TimerService object used for programmatic scheduling can be
* obtained either from the EJB context or it can be injected directly,
* as seen with the second instance variable below.
*
* Timer(s) created programmatically in an EJB will invoke one single
* callback method, either the ejbTimeout method from the TimedObject
* interface or a method annotated with @Timeout.
*/
@Singleton
@LocalBean
@Startup
public class ProgrammaticTimerStartedBean implements TimedObject
{
/* Constant(s): */
/* Instance variable(s): */
@Resource
private SessionContext mBeanContext;
@Resource
private TimerService mTimerService;
/**
* Initializes the EJB and starts the single-action timer that
* is to be invoked on this EJB.
* Programmatic creation of timers can be done in a method with
* any transaction attribute (container managed transactions).
*/
@PostConstruct
@TransactionAttribute(TransactionAttributeType.NEVER)
public void initialize()
{
System.out.println("ProgrammaticTimerStartedBean.initialize: "
+ new Date());
System.out.println("
Context: " + mBeanContext);
216
System.out.println("
Rollback only: "
+ mBeanContext.getRollbackOnly());
/*
* A TimerConfig object may be used to supply additional
* information that will be enclosed in the Timer object
* when the timeout callback method is invoked on the bean.
* The "Timer info" string in the example below is the arbitrary
* serializable object that we later can retrieve when the timeout
* callback method is invoked.
*/
TimerConfig theTimerConfig = new TimerConfig("Timer info", false);
/*
* Retrieve the timer service object from the bean context and
* create a timer.
* We could have used the timer service object in the instance
* variable mTimerService with the same effect.
*/
mTimerService.createIntervalTimer(5000, 5000, theTimerConfig);
}
/**
* Callback method in the TimedObject interface that is invoked when
* a timer associated with the instance expires.
* Such a method in an EJB with container managed transactions may
* only have the REQUIRED or REQUIRES_NEW transaction attributes.
* Successful execution of a timer callback method means that the
* transaction in which the method executes is committed.
*
* @param inTimer Timer that expired and caused invocation of the
* callback method.
*/
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void ejbTimeout(final Timer inTimer)
{
/*
* Output the time at which the method was invoked and the
* arbitrary serializable data enclosed when creating the
* TimerConfig and the timer.
*/
System.out.println("ProgrammaticTimerStartedBean.ejbTimeout: " +
new Date() + ", info: " + inTimer.getInfo());
/* Timer method always execute with an unauthorized principal. */
System.out.println("
Security principal: "
+ mBeanContext.getCallerPrincipal());
/*
* Retrieves the number of milliseconds remaining until the
* next invocation of the timeout callback method.
*/
System.out.println("
Time remaining: " + inTimer.getTimeRemaining());
/*
* Sometimes the transaction in which the timer callback is
* executed rolls back. This causes the container to retry the
* timer and, with GlassFish, eventually cancel the timer if
* a certain number of consecutive rollbacks occur.
*/
if (Math.random() > 0.8)
{
System.out.println("
Rolling back!");
mBeanContext.setRollbackOnly();
}
System.out.println("
Rollback only: "
+ mBeanContext.getRollbackOnly());
}
}
217
Note that:
The class is annotated with the @Singleton, the @LocalBean and the @Startup annotations.
The EJB is thus a singleton session bean with a local view that will be eagerly loaded upon
application startup.
The class has two instance variables, mBeanContext and mTimerService, both annotated
with the @Resource annotation.
We have already seen the EJB context of an EJB being injected into EJB instances. This
example also shows that an instance of TimerService can be injected into an EJB instance.
When creating the timer, a TimerConfig object is first created and the timer service is the
asked to create an interval timer.
The TimerConfig object enables us to set persistency of the timer as well as enclose a
serializable object holding additional data for our timeout callback method.
The class implements the TimedObject interface and contains a method named ejbTimeout.
The ejbTimeout method is the timeout callback method that will be invoked by the timer(s)
created programmatically in the EJB.
An alternative to the TimedObject interface is to annotate a method with the @Timeout
annotation.
The time remaining until the next invocation and other data related to the scheduling of the
timeout callback method can be retrieved from the Timer object. In this example we retrieve
the time in milliseconds that remain until the next invocation of the ejbTimeout method.
The transaction in which the ejbTimeout method is executing is randomly set to roll back.
We will see how this affects the timer when we run the example program.
218
12.1.5.
Having implemented the singleton session bean in the previous section we are now ready to try the
example program out. There is no need for a client to the EJB since, when it is deployed, the
singleton session bean will be eagerly initialized and the timer scheduled programmatically.
After some time, you should see output in the GlassFish log similar to this (part of the output
omitted to conserve space):
INFO: Portable JNDI names for EJB ScheduledStatelessSessionBean :
[java:global/TimerExample/ScheduledStatelessSessionBean!
com.ivan.scbcd6.ScheduledStatelessSessionBean,
java:global/TimerExample/ScheduledStatelessSessionBean]
INFO: Portable JNDI names for EJB ProgrammaticTimerStartedBean :
[java:global/TimerExample/ProgrammaticTimerStartedBean,
java:global/TimerExample/ProgrammaticTimerStartedBean!
com.ivan.scbcd6.ProgrammaticTimerStartedBean]
INFO: ProgrammaticTimerStartedBean.initialize: Wed Dec 08 19:38:53 CET 2010
INFO:
Context: ProgrammaticTimerStartedBean; id: [B@32f5c51c
INFO:
Rollback only: false
INFO: TimerExample was successfully deployed in 209 milliseconds.
INFO: ProgrammaticTimerStartedBean.ejbTimeout: Wed Dec 08 19:38:58 CET 2010, info: Timer
info
INFO:
Security principal: ANONYMOUS
INFO:
Time remaining: 4997
INFO:
Rollback only: false
...
INFO: ProgrammaticTimerStartedBean.ejbTimeout: Wed Dec 08 19:39:23 CET 2010, info: Timer
info
INFO:
Security principal: ANONYMOUS
INFO:
Time remaining: 4999
INFO:
Rolling back!
INFO:
Rollback only: true
INFO: ProgrammaticTimerStartedBean.ejbTimeout: Wed Dec 08 19:39:28 CET 2010, info: Timer
info
INFO:
Security principal: ANONYMOUS
INFO:
Time remaining: 4996
INFO:
Rolling back!
INFO:
Rollback only: true
INFO: EJB5119:Expunging timer ['12@@1291786701392@@server@@domain1' 'TimedObject =
ProgrammaticTimerStartedBean' 'Application = TimerExample' 'BEING_DELIVERED' 'PERIODIC'
'Container ID = 84658510992769024' 'Wed Dec 08 19:38:58 CET 2010' '5000' ] after [2]
failed deliveries
Note that:
The string Timer info is retrieved from the Timer object passed as parameter to the
ejbTimeout method.
This is the additional data enclosed to the TimerConfig object created when scheduling the
timer.
The security principal obtained in the ejbTimeout method is the principal representing an
unidentified user when using GlassFish v3 the name is ANONYMOUS.
After having set the transaction to roll back two consecutive times in a row, the timer is
cancelled by the container. No further invocations of the timeout callback method are made.
This feature is product specific and may differ between different application servers.
219
12.1.6.
Programmatic scheduling of timers can be part of a transaction and may thus be rolled back, as we
will see in this little addition to our example program.
...
/**
* Initializes the EJB and starts the single-action timer that
* is to be invoked on this EJB.
* Programmatic creation of timers can be done in a method with
* any transaction attribute (container managed transactions).
*/
@PostConstruct
@TransactionAttribute(TransactionAttributeType.NEVER)
public void initialize()
{
System.out.println("ProgrammaticTimerStartedBean.initialize: "
+ new Date());
System.out.println("
Context: " + mBeanContext);
System.out.println("
Rollback only: "
+ mBeanContext.getRollbackOnly());
/*
* A TimerConfig object may be used to supply additional
* information that will be enclosed in the Timer object
* when the timeout callback method is invoked on the bean.
* The "Timer info" string in the example below is the arbitrary
* serializable object that we later can retrieve when the timeout
* callback method is invoked.
*/
TimerConfig theTimerConfig = new TimerConfig("Timer info", false);
/*
* Retrieve the timer service object from the bean context and
* create a timer.
* We could have used the timer service object in the instance
* variable mTimerService with the same effect.
*/
Timer theTimer = mTimerService.createIntervalTimer(5000, 5000,
theTimerConfig);
/*
* Note that we can roll back timer creation despite the method
* having the transaction attribute NEVER.
*/
mBeanContext.setRollbackOnly();
System.out.println("
Creation rollback only: "
+ mBeanContext.getRollbackOnly());
System.out.println("
Timer next timeout: " + theTimer.getNextTimeout());
}
...
220
After some time, you should see output in the GlassFish log similar to this (part of the output
omitted to conserve space):
INFO: Portable JNDI names for EJB ScheduledStatelessSessionBean : ....
INFO: Portable JNDI names for EJB ProgrammaticTimerStartedBean : ...
INFO: ProgrammaticTimerStartedBean.initialize: Fri Dec 17 07:12:10 CET 2010
INFO:
Context: ProgrammaticTimerStartedBean; id: [B@57a8bdbe
INFO:
Rollback only: false
INFO:
Creation rollback only: true
INFO:
Timer next timeout: Fri Dec 17 07:12:15 CET 2010
INFO: TimerExample was successfully deployed in 254 milliseconds.
Note that:
A Timer object is obtained as a result of scheduling a timer.
Using this object we can get information about the timer.
The rollback-only flag is set to true when execution reaches the end of the initialize method.
According to the EJB 3.1 specification, the setRollbackOnly and getRollbackOnly methods
are not available in PostConstruct methods of stateless session beans since these methods
are executed in an unspecified transaction context. These methods should not be used as in
this example.
The timeout callback method is never called.
We are able to obtain a date telling us when the next timeout of the timer is to happen.
If we look at the API documentation for the getNextTimeout method in the Timer interface,
we can see that it will throw an exception if called when the timer is cancelled.
The timer has thus not been cancelled at this stage, but is cancelled sometime after the
initialize method has finished executing.
This concludes the scheduling EJB business logic example program.
221
12.2.
222
12.3.
An object implementing the Timer interface is obtained when scheduling a new timer. It may also
be enclosed as a parameter when the container invokes timeout callback methods.
The Timer interface contains the following methods:
Method Signature
Comments
void cancel()
TimerHandle getHandle()
Serializable getInfo()
Retrieves the timer info object set when the timer was created.
Date getNextTimeout()
ScheduleExpression getSchedule()
long getTimeRemaining()
boolean isCalendarTimer()
boolean isPersistent()
Returns true if the timer is persistent and thus will survive container
shutdown etc, false otherwise.
In addition to the above methods, objects implementing the Timer interface must also implement the
equals and hashCode methods, facilitating comparison etc of Timer objects.
223
12.3.1.
Timer Life-Cycle
A timer's life spans from the moment it is scheduled until after the last invocation of the timeout
callback method of the timer has finished executing or until the timer is cancelled.
If an attempt is made to invoke a method on a timer that no longer exists, a
NoSuchObjectLocalException will be thrown.
Timer lifecycle.
12.3.2.
Objects implementing the Timer interface must, according to the EJB 3.1 specification, not be
serializable. To serialize a timer, retrieve an object implementing the TimerHandle interface using
the getHandle method in the Timer interface.
A timer handle can be used during the lifetime of the timer from which it was obtained.
Persistent timers that are restored after, for instance, a container crash and that have expired during
the time when the container was down will have their timeout callback methods invoked as follows:
Single-event timers.
Timeout callback method will be invoked once.
Interval timer.
Timeout callback method will be invoked at least once, even if multiple expirations
occurred.
Scheduled timer.
Timeout callback method will be invoked at least once, even if multiple expirations
occurred.
224
12.4.
Declarative Scheduling
Declarative scheduling of business methods in EJBs can be accomplished using either annotations
or the deployment descriptor. Common to both kinds of declarative scheduling is that there may be
any number of timeout callback methods in an EJB that are invoked as a result of declarative
scheduling. It is also possible to have multiple timers scheduled declaratively to invoke one and the
same timeout callback method.
All timers created with declarative scheduling, be it using annotations or the deployment descriptor,
are calendar-based timers.
12.4.1.
To schedule a method in an EJB for invocation when timeout expiration event(s) occur, the method
just needs to be annotated with either the @Schedule annotation or, if multiple schedules are to be
used to activate one and the same method, the @Schedules annotation. Any number of methods in
an EJB can be scheduled this way, each with its own schedule if so desired.
The following annotations connected to declarative scheduling of business methods in EJBs are
available:
Annotation
Description
Schedule
Schedules
Allows for the scheduling of multiple timers to invoke one and the same timeout
callback method (the annotated method).
Timeout
Description
year
month
dayOfMonth
dayOfWeek
225
hour
minute
second
timezone
persistent
info
String that will be enclosed to the timeout callback method as timer info when it
is invoked.
Detailed information on scheduling with calendar-based expressions can be found in section 18.2.1
in the EJB 3.1 Specification.
226
12.4.2.
The features of the @Schedule and @Schedules annotations are also available using the ejb-jar.xml
deployment descriptor. The following figure shows the excerpt of the deployment descriptor XML
schema relevant to scheduling execution of business logic:
227
The following shows an example of declarative scheduling of business logic using the ejb-jar.xml
deployment descriptor:
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar version="3.1" 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_1.xsd">
<enterprise-beans>
<session>
<ejb-name>ScheduledStatelessSessionBean</ejb-name>
<local-bean/>
<ejb-class>com.ivan.scbcd6.ScheduledStatelessSessionBean</ejb-class>
<session-type>Stateless</session-type>
<!-Zero or more <timer> elements, each defining a timer
that schedules invocation of some business logic in the EJB.
-->
<timer>
<!-Specify the timer schedule.
-->
<schedule>
<second>15/10</second>
<minute>*</minute>
<hour>*</hour>
</schedule>
<!-An optional start and/or end time of the timer may be
specified. These times take precedence over the
schedule. Thus scheduled invocations of the timeout
callback method will occur according to the schedule
specified in the <schedule> element, but only inside
the time period specified by the <start> and <end>
times.
-->
<start>2010-12-01T00:00:00Z</start>
<end>2012-12-24T00:00:00Z</end>
<!-Specify the name and, optionally, the parameters
of the timeout callback method that is to be invoked
when the timer expires.
-->
<timeout-method>
<method-name>scheduledMethod1</method-name>
</timeout-method>
</timer>
</session>
</enterprise-beans>
</ejb-jar>
228
Note that:
Zero or more timers can be defined in the ejb-jar.xml deployment descriptor for an EJB.
The <timeout-method> element and child elements specifies the timeout callback method to
be invoked when the timer expires.
The <method-name> element contains the name of the timeout callback method. Optionally,
a <method-params> element containing one or more <method-param> elements may be
used to specify the method parameter types in order to distinguish methods with the same
names in the EJB.
229
12.5.
Programmatic Scheduling
Programmatic scheduling of timers is, as we have seen, accomplished using an object that
implements the TimerService interface. Such an object can be obtained in any of the following
ways:
Dependency injection.
JNDI lookup.
Calendar timers
Depending on their configuration, calendar timers can execute business logic once or at a
recurring interval.
Interval timers.
Interval timers will execute business logic at a recurring interval.
Single-action timers.
Single-action timers will execute the scheduled business logic once.
12.5.1.
Timeout Callback Methods for Programmatically Scheduled
Timers
Timeout callback methods of programmatically scheduled timers must follow the restrictions that
applies to all timeout callback methods as discussed earlier.
All timers created programmatically by an EJB will invoke one and the same timeout callback
method on the EJB. This timeout callback method can be specified in three ways:
Let the EJB implementation class implement the TimedObject interface and, in the EJB,
implement the ejbTimeout(Timer) method.
The first example shows an example of a timeout callback method specified using the @Timeout
annotation:
...
/**
* Processes timeout expiration events of programmatically scheduled timers
* in this EJB.
* Such a method in an EJB with container managed transactions may
* only have the REQUIRED or REQUIRES_NEW transaction attributes.
* Successful execution of a timer callback method means that the
* transaction in which the method executes is committed.
*
* @param inTimer Timer that expired and caused invocation of the
* callback method.
*/
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
@Timeout
public void myTimeout(final Timer inTimer)
{
...
230
The following example shows how the above method myTimeout is specified to be the timeout
callback method of the EJB ProgrammaticTimerStartedBean:
...
<session>
<ejb-name>ProgrammaticTimerStartedBean</ejb-name>
<!-Specifies the single timeout callback method to be invoked
when programmatically scheduled timers expire.
-->
<timeout-method>
<method-name>myTimeout</method-name>
<method-params>
<method-param>javax.ejb.Timer</method-param>
</method-params>
</timeout-method>
</session>
...
Note that, despite the EJB 3.1 specification saying that a <timeout-method> element without a
<method-params> element specified will match both a parameterless timeout callback method as
well as a timeout callback method taking a Timer object parameter, this is not the case with the
version of GlassFish v3 used when writing the examples of this book.
Timeout Callback Methods and the TimedObject Interface
The final example shows how the timeout callback method of an EJB is specified by implementing
the TimedObject interface:
public class ProgrammaticTimerStartedBean implements TimedObject
{
...
/**
* Callback method in the TimedObject interface that is invoked when
* a timer associated with the instance expires.
* Such a method in an EJB with container managed transactions may
* only have the REQUIRED or REQUIRES_NEW transaction attributes.
* Successful execution of a timer callback method means that the
* transaction in which the method executes is committed.
*
* @param inTimer Timer that expired and caused invocation of the
* callback method.
*/
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void ejbTimeout(final Timer inTimer)
{
...
}
...
}
231
12.5.2.
The programmatic version of the @Schedule annotation is calendar timers configured with a
ScheduleExpression instance. The configuration of the ScheduleExpression instance is similar to the
configuration of the @Schedule annotation described earlier with the same default values.
Depending on the schedule expression, the timer can be executed once or at a recurring interval.
The following example schedules a timer to execute every 15 th second, each minute in every hour:
...
TimerConfig theTimerConfig = new TimerConfig("Timer info", false);
/*
* Programmatically create a schedule expression that determines
* when the timer will expire.
* In this case, expiration will take place every 15th second
* in every minute in every hour.
*/
ScheduleExpression theTimerSchedule = new ScheduleExpression().
hour("*").minute("*").second(15);
Timer theTimer = mTimerService.createCalendarTimer(theTimerSchedule,
theTimerConfig);
...
In the above example, an optional TimerConfig object was also supplied. For details on timer
configuration, see below.
Specifying a Calendar with ScheduleExpression
The ScheduleExpression class enables specifying timer schedules that occurs at one single given
point in time or at a certain recurring interval. The ScheduleExpression class has the following
properties:
Property
Default
Allowed Values
Comments
Name
Value
timezone
startDate
none
endDate
none
year
month
1 to 12,
Jan to Dec
dayOfMonth
1 to 31,
-7 to -1,
Last,
1st to 5th,
Sun to Sat
dayOfWeek
0 to 7,
Sun to Sat
hour
0 to 23
minute
0 to 59
second
0 to 59
232
Also note that each setter-method comes in two versions; one that take a string as a parameter an
another that take an integer as a parameter.
Please refer to section 18.2.1 of the EJB 3.1 specification for additional details on schedule
expressions.
12.5.3.
Interval timers are timers which first expiration occurs after an initial delay and then expire at a
regular interval.
There are four methods in the TimerService interface that facilitates scheduling of interval timers:
createIntervalTimer(Date, long, TimerConfig)
createTimer(Date, long, Serializable)
createIntervalTimer(long, long, TimerConfig)
createTimer(long, long, Serializable)
The first two methods creates a timer that first expire at a point in time specified by the supplied
Date object and with subsequent expirations at an interval specified by the long integer parameter.
The latter two methods creates a timer that expires after the number of milliseconds specified by the
first long integer parameter and with subsequent expirations at an interval specified by the second
long integer parameter.
Timers are either configured according to the supplied TimerConfig object or with the supplied
serializable timer info object.
The following example schedules a timer that waits 42 milliseconds before first expiration and then
expire every 15th second.
...
/*
* Creates a timer that waits 42 milliseconds until first
* expiration and thereafter expires every 15th second.
* A null TimerConfig object is supplied, indicating that the
* default timer configuration is to be used.
*/
Timer theTimer = mTimerService.createIntervalTimer(42, 15000, null);
...
In the above example, a null TimerConfig object was supplied, indicating that the default timer
configuration is to be used.
233
12.5.4.
Single-action timers are timers that expires only once; either at a given point in time or after a
certain delay. There are four methods in the TimerService interface that facilitates scheduling of
single-action timers:
createSingleActionTimer(Date, TimerConfig)
createTimer(Date, Serializable)
createSingleActionTimer(long, TimerConfig)
createTimer(long, Serializable)
The first two methods creates a timer that expire at a point in time specified by the supplied Date
object. The latter two methods creates a timer that expires after the number of milliseconds
specified by the long integer parameter.
Timers are either configured according to the supplied TimerConfig object or with the supplied
serializable timer info object.
For details on timer configuration, see below.
The following example schedules a single execution of business logic 20 seconds into the future.
...
TimerConfig theTimerConfig = new TimerConfig("Timer info", false);
/*
* Creates a timer that will execute business logic once
* at a time specified by the supplied date.
*/
Date theTimerDate = new Date(System.currentTimeMillis() + 20000);
Timer theTimer = mTimerService.createSingleActionTimer(theTimerDate,
theTimerConfig);
...
12.5.5.
Timer Configuration
Timers are, as default, persistent. This means that they will survive the application server in which
the application runs being shut down. Creating non-persistent timers is possible by using one of the
methods that take a TimerConfig object as parameter. Supplying a TimerConfig also enables us to
supply a serializable object, a timer info object, that is later supplied every time the timeout callback
method is invoked.
...
/*
* Create a timer configuration object that sets the timer info
* to the string Timer info and makes the timer(s) created
* with the configuration non-persistent.
*/
TimerConfig theTimerConfig = new TimerConfig("Timer info", false);
/* Create a timer using the timer configuration. */
Timer theTimer = mTimerService.createIntervalTimer(5000, 5000,
theTimerConfig);
...
If a null TimerConfig object is supplied when creating a timer, the timer will be persistent and will
have a null timer info object.
234
12.6.
235
13.
References: EJB 3.1 Specification, chapters 19 and 20, JAR File Specification, JavaEE 6
Specification, chapter EE.8.
In this chapter we'll look at the options available when packaging and deploying modules that
contains EJBs - be it a complete application or a standalone module that only contains EJBs.
13.1.
Java EE modules are the basic building-blocks with which Java EE applications are assembled. Java
EE modules, in turn, are composed of Java EE components.
Java EE applications are contained in EAR files, where EAR stands for Enterprise Application
aRchive. Java EE applications may contain a number of libraries on which one or more modules in
the application may depend.
Java EE modules are:
EJB modules.
Contained in EJB-JAR files holding enterprise Java business components.
Libraries.
Contained in JAR files.
Java EE modules may contain a number of libraries on which one or more components in the
module may depend.
236
EJBs
Enterprise Java Beans. Commonly contains business logic.
Servlets
Includes JSP pages, JSF pages, filters and listeners.
Typically produces HTML that is rendered in a browser to create a user interface for the
application.
Application clients.
Client programs running on desktop computers. Typically so-called fat clients.
Applets.
User interface components executing in web browsers.
13.2.
Packaging Options
Starting with EJB 3.1, the following options how to package EJBs are available:
In EJB-JAR files.
This is the standard way of packaging EJBs and has also been used in earlier versions of the
EJB specification.
EJB-JAR files may in turn be packaged in EAR files.
In WAR files.
Starting with EJB 3.1, EJBs can now be packaged in web application archives.
WAR files may in turn be packaged in EAR files.
237
13.2.1.
EJB-JAR Files
EJB-JAR files are used to package standalone EJB modules. Such modules can be deployed
separately or be included in an EAR file. The following figure shows what the structure of an EJBJAR file may look like:
The MANIFEST.MF file is only required if the EJB needs to include classes in external JAR
files onto the classpath.
The ejb-jar.xml deployment descriptor is not required if all configuration of the EJBs in the
module has been done using annotations.
EJB implementation classes, interfaces and other classes can be included by including other
JAR files on the classpath.
Classes can be included either by being contained in the EJB-JAR file or by being contained
in another file that is referenced from the MANIFEST.MF file (included on the classpath).
An EJB-JAR file is a module with its own namespace, as described in the section on Portable JNDI
Names for Session Beans above. This means that the EJB-JAR file can be included in an EAR
(enterprise application archive) file together with other modules that contain components with the
same names as the components found in the EJB-JAR file without name collisions.
238
If the EJB-JAR file contains an ejb-jar.xml deployment descriptor for EJB 3.x and the
deployment descriptor is marked as metadata-complete, then annotations in classes in the
EJB-JAR file will not be processed.
If the EJB-JAR is included in, for instance, a WAR, then the metadata-completeness of its
deployment descriptor does not affect whether annotations of EJB classes in the WAR is
processed or not.
The following is an example of an EJB 3.1 deployment descriptor which has been marked as
being metadata-complete:
239
13.2.2.
WAR Files
Note that the above figure does not describe the complete structure of a WAR file.
As with EJB-JAR files, everything in a WAR file is optional:
The META-INF directory is only needed if the WAR is to contain a MANIFEST.MF file or
some other file that is to reside in this directory.
The MANIFEST.MF file is only required if the application needs to include classes in
external JAR files onto the classpath.
The ejb-jar.xml deployment descriptor, located in the WEB-INF directory, is not required if
all configuration of the EJBs in the module has been done using annotations.
EJB implementation classes, interfaces and other classes can be included by including other
JAR files on the classpath or in the WEB-INF/lib directory.
Classes can be included either by being contained in the WAR file or by being contained in
another file that is referenced from the MANIFEST.MF file (included on the classpath).
240
Additional factors that should be considered when deploying EJBs to WAR files:
If included, the ejb-jar.xml deployment descriptor applies to all the EJBs in the WAR file.
All components in a WAR file reside within one and the same module and share one and the
same (JNDI) naming environment.
Care should be taken as to avoid naming conflicts. For details on name context, please refer
to the section on Portable JNDI Names for Session Beans above.
EJB-JAR files located in the WEB-INF/lib directory will only be recognized as regular JAR
files and not as Java EE modules.
Any ejb-jar.xml deployment descriptors in such archives will not be recognized by the
container. Annotated EJB classes and interfaces in archives in the WEB-INF/lib directory
will be found and recognized by the container.
The no interface and local business interface view of an EJB deployed in a WAR file are
only visible to components within the WAR.
If the WAR file is part of an application and other modules of the application wishes to
access the no interface or local business interface view of an EJB, then the EJB must be
deployed in a separate EJB-JAR file in the application.
If the WAR file contains an ejb-jar.xml deployment descriptor for EJB 3.x in its WEB-INF
directory and the deployment descriptor is marked as metadata-complete, then annotations
in EJB classes in the WAR file will not be processed.
241
13.2.3.
JAR Files
13.2.4.
EAR Files
EAR, Enterprise Application aRchive, files are the top-level containers of Java EE applications.
When deployed in an EAR file, the global JNDI change to have the application name inserted. This
is described in the section on portable JNDI names earlier.
Starting from Java EE 5, the EAR application.xml deployment descriptor is optional and may be
left out.
If the application.xml deployment descriptor is used, the <library-directory> element may be used
to specify a directory of libraries. All JAR files in this directory are made available on the class-path
of all components of all components in the EAR. See the Java EE specification for details.
242
13.2.5.
Optionally, the classes and interfaces that make up the client view of an EJB can be packaged in a
separate JAR file - an EJB-Client JAR file. Such a JAR file contains all that clients of the EJB
needs to interact with the EJB. The client view of an EJB consists of the following:
Classes and interfaces used as method parameters in the EJB's business interface(s).
This includes classes used as members of collections that are method parameters.
Classes and interfaces used as method return values in the EJB's business interface(s).
This includes classes used as members of collections that are method return values.
Classes and interfaces used as method exceptions in the EJB's business interface(s).
In the module containing the EJB implementation, the location of the EJB-Client JAR file is
specified in the ejb-jar.xml deployment descriptor, relative to the archive containing the deployment
descriptor.
The deployment descriptor fragment below specifies the location of the EJB-Client JAR file having
the name employee_service_client.jar as being located at the same location in the containing EAR
file as the archive (EJB-JAR or WAR) that the deployment descriptor is contained in:
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar version="3.1" 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_1.xsd">
...
<!-The optional <ejb-client-jar> element specifies the location,
relative to the EJB-JAR or WAR file in which this
deployment descriptor is contained, in the EAR of the EJB-client
JAR file.
The EJB-client JAR contains all the classes and interfaces a
client needs to access the EJB(s) located in the archive in which
this deployment descriptor is located.
-->
<ejb-client-jar>employee_service-client.jar</ejb-client-jar>
</ejb-jar>
The contents of the EJB-Client JAR can be included in one of two ways:
By reference.
The client view classes and interfaces are contained in the EJB-Client JAR, which is
referenced from the EJB-JAR or WAR file containing the EJB implementation class(es) by
including the EJB-Client JAR on the classpath.
By copy.
The client view classes and interfaces are duplicated both in the EJB-Client JAR and in the
EJB-JAR or WAR in which the EJB implementation class(es) are located.
243
13.3.
Packaging Requirements
The following things are required to be included in, or referenced from, all the different packaging
archives in which EJBs can be distributed:
Interceptor classes.
If interceptors are configured.
Class files for all classes and interfaces the above classes depend on, except JavaSE and
JavaEE classes.
This includes, but is not limited to, EJB superclasses, method parameter classes, method
result parameter classes, exception classes.
13.4.
Module Visibility
Classes and interfaces in JAR files deployed to the lib directory of an EAR file or pointed out using
the <library-directory> element in the application.xml deployment descriptor will be made available
to all the modules in the EAR.
EJB components in EJB modules in an EAR file are accessible by other components in the EAR
without any addition class-path configuration.
Empirical results tell me that components in a web module in an EAR file are not accessible by
components outside of the web module.
13.5.
Bean provider.
Develops EJBs.
Application assembler.
Assembles the applications in which the EJBs are used.
244
The following EJB-related information may be modified when applications containing EJBs are
assembled:
Description fields.
The following EJB-related information must not be modified when applications containing EJBs are
assembled:
The following EJB-related information may optionally be specified when an application containing
EJBs is assembled:
Security roles.
Method permissions.
Interceptors.
Interceptor configurations may be changed or overridden, interceptors may be added or
reordered.
245
Part Two
Extras and Refactored Information
246
14.
Session Beans
Security.
Concurrency management.
Transactions.
247
14.1.
Despite fashionable options such as annotations, it is still possible to define an EJB using only the
ejb-jar.xml deployment descriptor and a Java class completely free from annotations, as shown in
this example:
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar version="3.1" 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_1.xsd">
<enterprise-beans>
<!-Minimal configuration to define a stateless session bean
with a no-interface view using only the deployment
descriptor and no annotations.
-->
<session>
<ejb-name>DDConfigedStatelessBean</ejb-name>
<local-bean/>
<ejb-class>com.ivan.scbcd6.DDConfigedStatelessBean</ejb-class>
<session-type>Stateless</session-type>
</session>
</enterprise-beans>
</ejb-jar>
For the curious, the corresponding class that implements the DDConfigedStatelessBean looks like
this:
package com.ivan.scbcd6;
public class DDConfigedStatelessBean
{
public String sayHello()
{
return "Hello!";
}
}
EJBs defined in the deployment descriptor are equal to annotated EJBs. For instance, they are also
eligible for dependency injection using annotated fields or methods in the EJB implementation
class.
248
The following XML schema fragment describes configuration options available when defining a
session bean in the ejb-jar.xml deployment descriptor:
Different sections of the deployment descriptor will be discussed further in relevant sections.
249
14.2.
Stateless
The bean does not contain state related to any particular client. Any instance serves any
client.
Stateful
The bean may contain state related to a particular client. A one-to-one relationship exists
between client and the bean that serves the requests of the client.
Singleton
A single instance shared by all clients. Allows for bean or container managed concurrent
access.
250
14.3.
Session bean metadata is data that specifies the type of a session bean, the view(s) it makes
available, the transaction policy, the life-cycle methods of the bean etc. Such data may be specified
using either annotations or the ejb-jar.xml deployment descriptor.
14.3.1.
To let the container know that a class we have written is to become a session bean, we can either
use the @Stateful, @Stateless or the @Singleton annotation or a deployment descriptor entry.
The @Stateful, @Stateless or the @Singleton are to annotate the class implementing the session
bean. They share the following optional elements:
Optional Element
Description
description
mappedName
name
To choose between the different types of session bean in the deployment descriptor, use the
<session-type> element with one of the following possible values:
Singleton
Stateful
Stateless
251
14.3.2.
The @LocalBean, @Local and @Remote annotations are used to specify the view(s) made
available by a session bean.
@LocalBean
Annotates the session bean implementation class that is to present a no-interface local view.
@Local
Annotates either the session bean implementation class or the local business interface(s) of a
session bean.
When annotating the session bean implementation class, the annotation declares the local
business interface(s) of the session bean. The value of the annotation lists the local business
interface(s).
When annotating one or more interfaces, it designates the interface(s) as a local business
interface(s). No value need to be provided.
@Remote
Annotates either the session bean implementation class or the remote business interface(s) of
a session bean.
When annotating the session bean implementation class, the annotation declares the remote
business interface(s) of the session bean. The value of the annotation lists the remote
business interface(s).
When annotating one or more interfaces, it designates the interface(s) as a remote business
interface(s). No value need to be provided.
In the deployment descriptor, the <local-bean>, <local> and <remote> elements can be used to
specify the different kinds of views. See example in the previous section.
252
14.3.3.
Comments
@PostConstruct
@PreDestroy
@PrePassivate
@PostActivate
The above annotations available in EJB 3.0 and later eliminates the need for implementing the
javax.ejb.SessionBean interface with its lifecycle callback methods.
253
14.4.
In this section, an example program showing concurrency behaviour of stateful and stateless session
beans will be examined. The concurrency behaviour of singleton session beans is discussed
extensively in the Singleton Session Bean Concurrency section in chapter four.
14.4.1.
First the example program showing the concurrent behaviour of stateful session beans is
implemented. We'll later modify this program slightly to look at the concurrent behaviour of
stateless session beans.
In the package com.ivan.scbcd6, create the session bean implementation class according to
the following listing:
package com.ivan.scbcd6;
import javax.ejb.Stateful;
/**
* This class implements a session bean on which concurrent
* access will be tested.
*/
@Stateful
public class SessionBeanA
{
public void slowMethod()
{
System.out.println("SessionBeanA - Entering slowMethod " +
this);
waitSomeTime(10);
System.out.println("SessionBeanA - Exiting slowMethod " +
this);
}
public void fastMethod()
{
System.out.println("SessionBeanA - Entering fastMethod " +
this);
waitSomeTime(1);
System.out.println("SessionBeanA - Exiting fastMethod " +
this);
}
private void waitSomeTime(final long inSecondsDelay)
{
try
{
Thread.sleep(1000L * inSecondsDelay);
} catch (InterruptedException e)
{
// Ignore exceptions.
}
}
}
254
In the package com.ivan.scbcd6.client, create the servlet that will act as a client to the
session bean:
package com.ivan.scbcd6.client;
import
import
import
import
import
import
import
import
java.io.IOException;
java.io.PrintWriter;
javax.ejb.EJB;
javax.servlet.ServletException;
javax.servlet.annotation.WebServlet;
javax.servlet.http.HttpServlet;
javax.servlet.http.HttpServletRequest;
javax.servlet.http.HttpServletResponse;
import com.ivan.scbcd6.SessionBeanA;
/**
* Servlet acting as a client of the session bean.
*/
@WebServlet(name = "SessionBeanClientServlet", urlPatterns = "/test.do")
public class SessionBeanClientServlet extends HttpServlet
{
/* Constant(s): */
private static final long serialVersionUID = 1L;
/* Instance variable(s): */
@EJB
private SessionBeanA mSessionBean;
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
@Override
protected void doGet(HttpServletRequest inRequest,
HttpServletResponse inResponse) throws ServletException, IOException
{
System.out.println("*** Entering SessionBeanClientServlet");
testConcurrentAccess();
System.out.println("*** Exiting SessionBeanClientServlet");
/* Display a message on the web page. */
PrintWriter theResponseWriter = inResponse.getWriter();
theResponseWriter.println("Finished invoking session bean " +
"concurrency test.");
}
private void testConcurrentAccess()
{
System.out.println("*** Entering testConcurrentAccess");
/*
* Prepare the two threads from which the different methods
* of the stateful session bean will be called.
*/
Thread theSlowMethodThread = new Thread()
{
@Override
public void run()
{
mSessionBean.slowMethod();
}
};
Thread theFastMethodThread = new Thread()
{
@Override
public void run()
{
mSessionBean.fastMethod();
}
};
System.out.println("
Calling slowMethod...");
255
theSlowMethodThread.start();
System.out.println("
Calling fastMethod...");
theFastMethodThread.start();
System.out.println("*** Exiting testConcurrentAccess");
}
}
Note that:
The session bean is stateful.
The execution of the method slowMethod does not overlap with the execution of the
fastMethod.
Both the fastMethod and the slowMethod are invoked on the same session bean instance.
If we try to annotate the session bean class with the
@ConcurrencyManagement(ConcurrencyManagementType.BEAN) annotation, we can see
that the behaviour of the bean does not change.
The conclusion is that bean-managed concurrency does not apply to stateful session beans.
For details on the ConcurrencyManagement annotation, please see the section on
Concurrency Management in the chapter on singleton session beans.
256
14.4.2.
To look at the concurrent behaviour of stateless session beans, we simply change the annotation on
the bean class in the example program from the previous section:
...
@Stateless
public class SessionBeanA
{
...
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
Note that:
The session bean is stateless.
The execution of the method slowMethod overlaps with the execution of the fastMethod.
The fastMethod and the slowMethod are invoked on different session bean instances.
We can conclude that the container does not allow concurrent access of a stateless session
bean instance. If a stateless session bean instance is busy serving a request (fastMethod),
another instance is fetched from the pool and the second request (slowMethod) is invoked on
the second instance.
If we try to annotate the session bean class with the
@ConcurrencyManagement(ConcurrencyManagementType.BEAN) annotation, we can see
that the behaviour of the bean does not change.
The conclusion is that bean-managed concurrency does not apply to stateless session beans.
For details on the ConcurrencyManagement annotation, please see the section on
Concurrency Management in the chapter on singleton session beans.
14.5.
257
15.
Examples
Comments
EJB References
@WebServiceRef MyService
serviceRef;
@Resource javax.sql.DataSource
myAppDB;
@Resource SomeRsrcEnvRef
myRsrcEnvRef;
@Resource javax.jms.Queue
workQueue;
@PersistenceUnit(unitName="Wareh
ouseManagement")
EntityManagerFactory
warehouseEMF;
@PersistenceContext(type=EXTEND
ED) EntityManager myEntityMgr;
UserTransaction Interface
@Resource UserTransaction
transaction;
ORB References
TimerService References
@Resource TimerService
mTimerService;
EJBContext References
@Resource SessionContext
mSessionBeanContext;
258
15.1.
Commonly used for injection or declaration of various things is the @Resource annotation. This
annotation may be applied to:
A class.
Declaring a resource that the application will look up at runtime.
An instance field.
Declaring a field, with any visibility modifier, that is to be injected with the specified
resource when an instance of the class is initialized.
A setter method.
Declares a method that will set specified resource when an instance of the class is initialized.
Comments
authenticationType
description
mappedName
name
shareable
type
For some resource types, the shareable and authenticationType elements must not be used, since
resources of that type does not require authentication and are not shareable.
259
15.2.
The @EJB annotation can be applied at the following targets with the corresponding result:
Target of @EJB Annotation
Effect
Class
Method
Instance field
Entries declared in the annotated beans environment using an @EJB annotation is to be looked up
by the bean using either the EJBContext.lookup method or the JNDI API.
Optional elements of the @EJB annotation are:
Element
Type
Description
beanInterface
Class
beanName
String
description
String
lookup
String
mappedName
String
Product-specific, non-portable,
name of the EJB to which this
reference is mapped.
name
String
When using the @EJB annotation to perform dependency injection, either the beanName or the
lookup element, but not both elements, can be used to resolve the dependency.
260
15.2.1.
15.2.2.
In the second example, a setter method setting a reference to another EJB is annotated. The method
will be invoked after an instance of the EJB implementation class is created and before it is taken
into use. Dependency injection takes place prior to any @PostConstruct method is being invoked.
As an alternative to the beanName element in the @EJB annotation, one of the lookup or name
elements can be used specifying either the full JNDI name or the JNDI name relative to
java:comp/env used to look up the bean reference to be injected. In the case of no ambiguity, all
elements can be omitted.
...
@Stateful
@LocalBean
public class StatefulSession1Bean
{
...
@EJB(beanName="DummyStatelessBean")
public void setDummyReference(DummyStatelessBean inBeanReference)
{
// Store the reference to the injected bean.
}
...
15.2.3.
In the final example, an instance field holding a reference to another EJB is annotated. Dependency
injection into instance fields also takes place prior to the invocation of any @PostConstruct method.
As in the case of annotating a setter-method, the beanName, name or lookup elements of the @EJB
annotation may be used to specify which reference to inject. Again, in the case of no ambiguity, all
elements can be omitted.
@Stateful
@LocalBean
public class StatefulSession1Bean
{
@EJB
private DummyStatelessBean mDummyStatelessRef;
...
261
15.3.
Injection of resource references can be accomplished without annotations, using the ejb-jar.xml
deployment descriptor. Since this is common to all the different resource reference types, it is
described in this, separate, section.
The <injection-target> element appears as a child element of an element defining a resource
reference and specifies the class and instance field name in which the resource reference is to be
injected. The fragment of the XML schema defining the <injection-target> element looks like this:
The fragment from the ejb-jar deployment descriptor XML schema defining the <injection-target>
element and child elements, which are used to perform dependency injection from the deployment descriptor.
Example:
Define the field to be injected in the EJB. Note that the field is not annotated.
...
/* Simple Environment Entry Injection without Annotations. */
private int turboCounter = 99;
...
Specify the value to inject and where in the EJB the value is to be injected in the ejb-jar.xml
deployment descriptor:
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar version="3.1" 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_1.xsd">
<enterprise-beans>
<session>
<ejb-name>StatefulSession2Bean</ejb-name>
...
<env-entry>
<env-entry-name>turboCounter</env-entry-name>
<env-entry-type>java.lang.Integer</env-entry-type>
<env-entry-value>15</env-entry-value>
<!-Specifies where to inject the value/resource reference.
-->
<injection-target>
<injection-target-class>
com.ivan.scbcd6.StatefulSession2Bean
</injection-target-class>
<injection-target-name>
turboCounter
</injection-target-name>
</injection-target>
</env-entry>
262
</session>
</enterprise-beans>
</ejb-jar>
Since a value was provided as an initialization value of the instance field, this is the value the field
will have if it is not injected with a value in the ejb-jar.xml deployment descriptor. Be aware that if
no value is provided in the deployment descriptor, then trying to look up the value using the JNDI
API will fail with an error.
In the above example, the message destination reference is injected into the instance field
turboCounter in the class com.ivan.scbcd6.StatefulSession2Bean.
With resource references, the type of the instance field must match the resource reference type.
15.4.
Another thing common to different kinds of resources is programmatic lookup. In order to be able
to perform programmatic lookup, a reference has to be specified in the ejb-jar.xml deployment
descriptor or a JNDI name has to be assigned to the resource in an annotation.
When looking up a resource using its JNDI name, there are two options for performing a lookup
from within an EJB:
Below are examples of how to use each method to lookup a resource from within an EJB.
15.4.1.
Looking up a JNDI name using an instance of InitialContext is quite simple. The example below is
from an EJB running in the GlassFish v3 application server:
Context theInitialContext = new InitialContext();
Context theJavaCompEnv = (Context)theInitialContext.lookup("java:comp/env");
ReferencedLocal theLocalRef =
(ReferencedLocal)theJavaCompEnv.lookup("ejb/AnotherBean");
In the above example, an InitialContext is created and, using that context, the java:comp/env subcontext is looked up. The sub-context is then used to look up a reference to an EJB implementing
the ReferencedLocal interface.
References may, of course, be looked up using the InitialContext instance directly, eliminating the
step in which the sub-context is retrieved.
263
15.4.2.
When using an EJBContext to look up resource references, we must first see to that an instance of
the context is injected into the EJB in which we want to perform the lookup. This is accomplished
by creating an instance field and annotating it with the @Resource annotation:
@Stateful
public class StatefulSession2Bean
{
@Resource
private SessionContext mSessionBeanContext;
...
There are four different types of EJB contexts that can be injected.
EJB Context Type
Comments
EJBContext
EntityContext
Entity beans.
MessageDrivenContext
No additional functionality
compared to EJBContext.
SessionContext
Session beans.
For complete details on the different context-types, please refer to the EJB 3.1 API documentation.
Available in the EJBContext, and thus in all types of contexts, is the lookup method, which we can
use to look up a resource references. Three things should be taken into consideration when using the
EJBContext.lookup method:
If looking up names referring to entries within the private component namespace, for
instance the reference to the local view of an EJB, then the java:comp/env/ part of the
JNDI name can be omitted.
As a consequence of the previous point; when looking up entries not within the private
component namespace, then entire JNDI names must be used.
Both the above references were declared in the ejb-jar.xml deployment descriptor; the first using an
<env-entry> element and the second using an <ejb-local-ref> element.
264
15.5.
265
15.5.1.
When using the @Resource annotation to annotate an instance field or setter-method to facilitate
injection of simple environment entries, the authenticationType and shareable elements of the
annotation must not be used, since simple environment entries neither require authentication, nor
are they shareable.
Example:
Annotate the field to be injected in the EJB:
...
/* Simple Environment Entry Injection. */
@Resource(name="turboFlag")
private boolean mTurboFlag;
...
A value must also be specified in the deployment descriptor, as showed in the next section.
15.5.2.
Defining Simple Environment Entries in the Deployment
Descriptor
Specify the value to inject in the ejb-jar.xml deployment descriptor using an <env-entry> element in
the bean that the value is to be injected into:
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar version="3.1" 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_1.xsd">
<enterprise-beans>
<session>
<ejb-name>StatefulSession2Bean</ejb-name>
<!-- Simple environment entry. -->
<env-entry>
<env-entry-name>turboFlag</env-entry-name>
<env-entry-type>java.lang.Boolean</env-entry-type>
<env-entry-value>true</env-entry-value>
</env-entry>
</session>
</enterprise-beans>
</ejb-jar>
Specifying the instance field in which the environment entry is to be injected into can also be
specified in the deployment descriptor using the <injection-target> element, as previously
described.
15.5.3.
Finally, simple environment entries can be looked up as described in the section on Programmatic
Lookup of References from EJBs above.
266
15.6.
EJB References
15.6.1.
Injection of references to other EJBs can be injected into an EJB into fields annotated using the
@EJB annotation. Detailed information on the @EJB annotation can be found in section 5.3.1.
When injecting an EJB reference, a field or setter method is annotated. The type of the field or the
type of the parameter of the setter method can be a no-interface view, a local or remote business
interface.
Example:
If we want to provide a custom name of the EJB which reference is to be injected, we use the
optional name element in the @Stateful or @Stateless annotation:
...
@Stateless(name="ReferencedBean")
public class ReferencedBean implements ReferencedLocal {
...
The field or setter method to be injected is annotated with the @EJB annotation, specifying the
name of the EJB which reference to be injected using the beanName optional element:
...
/* EJB Reference Injection. */
@EJB(beanName="ReferencedBean")
private ReferencedLocal mReferencedBean;
...
267
15.6.2.
EJB references can be defined in the deployment descriptor using the <ejb-ref> or <ejb-local-ref>
elements. The XML schema fragment defining the <ejb-ref> element looks like this:
Annotating a class with the @EJB annotation can also be used to declare references to other EJBs.
268
269
Example:
In the ejb-jar.xml deployment descriptor, the following configuration is made for a reference to an
EJB with a local view:
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar version="3.1" 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_1.xsd">
<enterprise-beans>
<session>
<!-Supplying the name of the EJB which we want override the
annotation configuration of is enough.
-->
<ejb-name>StatefulSession2Bean</ejb-name>
...
<ejb-local-ref>
<!-Desired JNDI name. "java:comp/env/" is automatically
prefixed. Must be specified.
-->
<ejb-ref-name>AnotherReferencedBeanRef</ejb-ref-name>
<!-- Specifies name of EJB to inject reference to. -->
<ejb-link>AnotherReferencedBean</ejb-link>
<!-Instead of using the <ejb-link> element above, the following
element can be used to determine which EJB's reference to
inject (the two elements are mutually exclusive):
<lookup-name>
java:global/EJBDependencyInjection/AnotherReferencedBean
</lookup-name>
-->
</ejb-local-ref>
</session>
</enterprise-beans>
</ejb-jar>
The EJB reference defined above can then, for instance, be programmatically looked-up, as
described in the next section.
As with other references, it is also possible to specify the instance field in which the EJB reference
is to be injected into using the <injection-target> element in the ejb-jar.xml deployment descriptor,
as previously described.
15.6.3.
270
15.6.4.
Overriding an @EJB Annotation with the Deployment
Descriptor
The following rules exist regarding overriding an @EJB annotation with a deployment descriptor:
Any description in the deployment descriptor overrides the description element in the @EJB
annotation.
271
15.7.
15.8.
15.8.1.
Using the @Resource annotation on an instance field or a setter-method allows for references to
resource manager connection factories to be injected into EJBs.
The authenticationType element can be used to specify whether the container or the application is to
be responsible for authentication. The shareable element indicates whether connections obtained
from the factory can be shared or not.
Example:
@Stateless
public class WarehouseInventoryBean
{
...
/* Warehouse DB connection factory. */
@Resource(name=jdbc/WarehouseAppDB)
javax.sql.DataSource mWarehouseAppDB;
...
...
public int countItems(ItemId inItemId)
{
...
java.sql.Connection theConnection = mWarehouseAppDB.getConnection();
/* Now we can query the database. */
...
}
272
15.8.2.
Defining Resource Manager Connection Factory References
in the Deployment Descriptor
Resource manager connection factory references are defined in the ejb-jar.xml deployment
descriptor using <resource-ref> elements. The XML schema fragment specifying the structure of
the <resource-ref> element looks like this:
The fragment from the ejb-jar deployment descriptor XML schema defining the <resource-ref> element,
which is used to define resource manager connection factory references.
273
The following listing shows an example of an ejb-jar.xml deployment descriptor with a resource
manager connection factory reference declared in the StatefulSession2Bean session bean:
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar version="3.1" 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_1.xsd">
<enterprise-beans>
<session>
<ejb-name>StatefulSession2Bean</ejb-name>
<!-Resource manager connection factory reference to
a database (JDBC connection pool).
-->
<resource-ref>
<res-ref-name>jdbc/WarehouseAppDB</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<!-Configures how resource manager authentication is configured:
- Container = the deployer configures the resource manager factory
with authentication information. EJBs using the resource manager
factory does not need to provide authentication information.
- Application = the EJB programmatically authenticates with
the resource manager factory.
-->
<res-auth>Container</res-auth>
<!-A resource connection can be Shareable or Unshareable,
which indicates whether a connection obtained from the
connection factory can be shared by other EJBs in the same
application using the same resource in the same transaction
context.
Default is Shareable.
-->
<res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>
...
</session>
</enterprise-beans>
</ejb-jar>
As with other references, it is also possible to specify the instance field in which the resource
manager connection factory reference is to be injected into using the <injection-target> element in
the ejb-jar.xml deployment descriptor, as previously described.
274
15.8.3.
Programmatic Access of Resource Manager Connection
Factory References
In order to be able to retrieve and use a resource manager connection factory programmatically
from within the code of an EJB, the bean provider must follow this procedure:
It is recommended, but not mandated, that resource manager connection factory references
are organized into different subcontexts, depending on the resource type. For instance:
- JDBC DataSource declared in the java:comp/env/jdbc subcontext.
- JMS connection factories declared in the java:comp/env/jms subcontext.
- JavaMail connection factories declared in the java:comp/env/mail subcontext.
- URL connection factories declared in the java:comp/env/url subcontext.
In the code, look up the resource manager connection factory object using either the JNDI
API or the lookup method in the EJBContext.
Invoke the resource manager connection factory to obtain one or more connections to the
resource. Example:
@Stateless
public class WarehouseInventoryBean
{
...
/* EJB context object used to lookup resource references. */
@Resource
private SessionContext mSessionContext;
...
...
public int countItems(ItemId inItemId)
{
...
/*
* Lookup the resource manager connection factory using its
* JNDI name. Note that the name is automatically prefixed with
* java:comp/env/.
*/
javax.sql.DataSource theWarehouseAppDB =
mSessionContext.lookup("jdbc/EmployeeAppDB"));
/* Retrieve a resource (database) connection. */
java.sql.Connection theConnection = theWarehouseAppDB.getConnection();
/* Now we can query the database. */
...
}
...
}
275
15.9.
15.9.1.
Injecting a Resource Environment Reference Using
Annotations
To inject the resource environment reference created above using annotations, all that is needed is to
annotate an instance field or a setter-method with the @Resource annotation, specifying the JNDI
name of the resource reference to inject:
...
@Stateful
@LocalBean
public class StatefulSession2Bean
{
...
/* Resource environment reference with annotation. */
@Resource(name="resref/MyRsrcEnvRef1")
private Properties mResourceEnvRef1;
...
}
276
15.9.2.
Defining Resource Environment References in the
Deployment Descriptor
Resource environment references are defined using <resource-env-ref> element in the ejb-jar.xml
deployment descriptor. The <resource-env-ref> element has the following structure:
The fragment from the ejb-jar deployment descriptor XML schema defining the <resource-ref> element,
which is used to define resource manager connection factory references.
277
The resource environment reference must also be defined in the application server in which the EJB
is to be executed. For a description on how to define such references in GlassFish v3, see appendix
E.
278
15.9.3.
Programmatically Retrieving Resource Environment
References
Resource environment references can be looked up as described in the section on Programmatic
Lookup of References from EJBs above. The procedure for defining and accessing a resource
environment reference is:
From within the EJB, look up the resource reference using either the JNDI API or an
EJBContext object.
279
15.10.
280
The XML schema fragment defining message destination references in the deployment descriptor
looks like this:
The fragment from the ejb-jar deployment descriptor XML schema defining the <message-destination-ref>
element, which is used to define message destination references.
The following listing shows an example of a message destination reference declaration in an ejbjar.xml deployment descriptor:
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:ejb="http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd"
xsi:schemaLocation="
http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd"
version="3.1">
<enterprise-beans>
<message-driven>
<ejb-name>ListenerFacadeMDB</ejb-name>
<message-destination-link>ListenerMsgDest</message-destination-link>
<message-destination-ref>
<!-JNDI name used in the EJB to look up the destination
reference. Note that this name does not have to correspond
to a destination in the application server if a message
destination link is used.
When a message destination link is used, this reference
has to be looked up programmatically or being injected
from the deployment descriptor and cannot be
injected using annotations.
-->
<message-destination-ref-name>
jms/PublishingTopic
</message-destination-ref-name>
281
Please refer to appendix C for a complete example on using message destination links.
282
When using message destination links, the JNDI name specified in a <message-destination-refname> element must be looked-up programmatically or injected using the <injection-target> child
element of the <message-destination-ref> element in the deployment descriptor, as described
earlier.
283
15.11.
15.11.1.
If, for some reason, we do not want to use annotations to inject a reference to a persistence unit,
then we need to declare it in the ejb-jar.xml deployment descriptor, in order to be able to look it up
programmatically in an EJB.
The XML schema fragment defining persistence unit references in the deployment descriptor looks
like this:
The fragment from the ejb-jar deployment descriptor XML schema defining the <persistence-unit-ref> element,
which is used to define persistence unit references.
284
When overriding a @PersistenceUnit annotation using the ejb-jar.xml deployment descriptor, the
following rules exists:
The container uses the JNDI name, be it the default name or a provided name, of the
@PersistenceUnit annotation to determine whether a <persistence-unit-ref> entry in the
deployment descriptor is to override the annotation.
The value of the unitName element of the @PersistenceUnit annotation is overridden by the
value in the <persistence-unit-name> element.
The injection target, if used, must specify the exact name of the annotated field or property
method.
15.11.2.
Optionally, the name element in the annotation may be used to define a JNDI name by which the
reference can be looked up programmatically. In the above example, a reference to the entity
manager factory can be looked up using the JNDI name
java:comp/env/persistence/MyEntityMgrFactory.
285
15.11.3.
Persistence Unit References can be looked up as described in the section on Programmatic Lookup
of References from EJBs above.
15.12.
286
The fragment from the ejb-jar deployment descriptor XML schema defining the <persistence-context-ref> element,
which is used to define persistence context references.
287
The container uses the JNDI name, be it the default name or a provided name, of the
@PersistenceContext annotation to determine whether a <persistence-context-ref> entry in
the deployment descriptor is to override the annotation.
The injection target, if used, must specify the exact name of the annotated field or property
method.
288
15.12.2.
Optionally, the name element in the annotation may be used to define a JNDI name by which the
reference can be looked up programmatically. In the above example, a reference to the entity
manager factory can be looked up using the JNDI name java:comp/env/persistence/MyEntityMgr.
The type element of the @PersistenceContext annotation is used to specify the scope of the
EntityManager injected. The default value is PersistenceContextType.TRANSACTION. Possible
values are:
Persistence Context Type
Notes
PersistenceContextType.TRANSACTION
PersistenceContextType.EXTENDED
The properties element enables specifying name-value pairs for properties that are passed to the
container or persistence provider. Properties with names that are not recognized are ignored.
289
15.13.
UserTransaction Interface
290
15.13.2.
15.13.3.
15.14.
ORB References
15.14.1.
ORB references are declared in the ejb-jar.xml deployment descriptor in the same way as resource
manager connection factories.
The example configuration below injects an ORB reference into the instance field
mDDInjectedCorbaOrb in the EJB with the name StatefulSession2Bean:
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar version="3.1" 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_1.xsd">
<enterprise-beans>
<session>
<!-Supplying the name of the EJB which we want override the
annotation configuration of is enough
-->
<ejb-name>StatefulSession2Bean</ejb-name>
<!-- Inject an ORB reference into the EJB. -->
<resource-ref>
<res-ref-name>java:comp/ORB</res-ref-name>
<res-type>org.omg.CORBA.ORB</res-type>
<!-Request a reference to an ORB instance that is not
shared.
Default is for ORB instances to be shared.
-->
291
<res-sharing-scope>Unshareable</res-sharing-scope>
<!-- Specifies where to inject the value. -->
<injection-target>
<injection-target-class>
com.ivan.scbcd6.StatefulSession2Bean
</injection-target-class>
<injection-target-name>mDDInjectedCorbaOrb</injection-target-name>
</injection-target>
</resource-ref>
...
</session>
</enterprise-beans>
</ejb-jar>
In the above example, a reference to an ORB instance that is not shared is request by using the <ressharing-scope> element with the value Unshareable.
15.14.2.
15.14.3.
292
15.15.
TimerService References
15.15.1.
TimerService references are declared in the ejb-jar.xml deployment descriptor in the same way as
resource environment references.
The example configuration below injects a TimerService reference into the instance field
mDDInjectedTimerService in the EJB with the name StatefulSession2Bean:
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar version="3.1" 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_1.xsd">
<enterprise-beans>
<session>
<!-- Name of EJB in which to inject the reference. -->
<ejb-name>StatefulSession2Bean</ejb-name>
<!-- Inject reference to TimerService. -->
<resource-env-ref>
<!-- JNDI name of the reference. Required. -->
<resource-env-ref-name>java:comp/TimerService</resource-env-ref-name>
<!-- Type of the reference. Optional. -->
<resource-env-ref-type>javax.ejb.TimerService</resource-env-ref-type>
<!-- Specifies instance field to inject the reference into. -->
<injection-target>
<injection-target-class>
com.ivan.scbcd6.StatefulSession2Bean
</injection-target-class>
<injection-target-name>
mDDInjectedTimerService
</injection-target-name>
</injection-target>
</resource-env-ref>
...
</session>
</enterprise-beans>
</ejb-jar>
15.15.2.
293
15.15.3.
15.16.
EJBContext References
15.16.1.
EJBContext references are declared in the ejb-jar.xml deployment descriptor in the same way as
resource environment references.
The example configuration below injects a EJBContext reference into the instance field
mDDInjectedEjbContext in the EJB with the name StatefulSession2Bean:
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar version="3.1" 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_1.xsd">
<enterprise-beans>
<session>
<!-- Name of EJB in which to inject the reference. -->
<ejb-name>StatefulSession2Bean</ejb-name>
<!-- Inject reference to EJBContext. -->
<resource-env-ref>
<!-- JNDI name of the reference. Required. -->
<resource-env-ref-name>java:comp/EJBContext</resource-env-ref-name>
<!-- Type of the reference. Optional. -->
<resource-env-ref-type>javax.ejb.EJBContext</resource-env-ref-type>
<!-- Specifies instance field to inject the reference into. -->
<injection-target>
<injection-target-class>
com.ivan.scbcd6.StatefulSession2Bean
</injection-target-class>
<injection-target-name>mDDInjectedEjbContext</injection-target-name>
</injection-target>
</resource-env-ref>
...
</session>
</enterprise-beans>
</ejb-jar>
294
15.16.2.
15.16.3.
295
16.
Exceptions from EJBs categorized into system exceptions and application exceptions.
Further details about application and system exceptions in the upcoming sections.
296
16.1.
Application Exceptions
Application exceptions are exceptions defined by the bean provider used to report problems
connected to the business logic of an application. An application exception does not necessarily
cause a business process to be aborted or rolled back. Clients of EJBs are expected to be able to
catch, and recover from, application exceptions.
Application exceptions are:
The class java.lang.Exception and subclasses.
Excluded are java.rmi.RemoteException and subclasses, which are system exceptions.
Unchecked exceptions (java.lang.RuntimeException and subclasses) annotated with the
@ApplicationException or marked with the <application-exception> element in the ejbjar.xml deployment descriptor and listed in the throws-clause of one or more business
methods.
The class javax.ejb.CreateException and subclasses.
The class javax.ejb.RemoveException and subclasses.
The class javax.ejb.FinderException and subclasses.
Application exceptions are typically included in the throws clause of methods in the different kinds
of views that can be presented by an EJB.
The @ApplicationException annotation is used to mark an exception as being an application
exception and that it should be reported to the client without being wrapped. The annotation has the
following optional elements:
Element
Description
inherited
rollback
16.1.1.
Application exceptions does not automatically result in the transaction in which the exception was
thrown as being marked for rollback unless the rollback element of the @ApplicationException
annotation or corresponding configuration in the deployment descriptor has the value true.
Prior to throwing an application exception, the bean provider must do one of the following:
Ensure that the state of the EJB instance is consistent.
This in order to allow for continued use of the EJB instance.
Mark the transaction for rollback.
Not necessary if the exception thrown will cause transaction rollback.
This in order to prevent that the transaction is committed and thereby causing loss of data
integrity.
297
16.2.
System Exceptions
System exceptions usually indicate that there was an unknown problem that the EJB or the EJB
container throwing the exception were not able to recover from or that the problem was an
unexpected one.
System exceptions are:
The java.rmi.RemoteException and subclasses.
Unchecked exceptions (java.lang.RuntimeException and subclasses) that are not annotated
with the @ApplicationException and not marked with the <application-exception> element
in the ejb-jar.xml deployment descriptor.
Such exceptions should not be listed in the throws-clauses of business methods.
The EJB specification gives the following recommendations regarding how exceptions should be
handled in an EJB:
System exceptions should be propagated to the container.
That is, there is no need to catch and try to handle system exceptions in an EJB.
If the EJB is unable to recover from a checked exception, it should be wrapped in an
EJBException and the EJBException thrown.
Other unexpected errors should result in EJBException-s being thrown.
Note that EJBException is an unchecked exception.
An EJB instance throwing a system exception will be destroyed, unless it is a singleton session
bean. Thus only singleton session beans need to make sure the state of the EJB is consistent before
throwing a system exception, since the bean will continue to serve any future requests.
An attempt to invoke a stateful session bean having thrown a system exception will result in a
NoSuchEJBException being thrown.
16.2.1.
The container will automatically roll back any transaction the EJB were executing in if a system
exception is thrown from within a method in the EJB.
16.3.
The figures in this section show how exceptions from session bean methods in different views are
handled, how transactions are affected and what exceptions clients receives.
Omitted from the diagrams are logging to the container's log, which always takes place in the case
of system exceptions.
298
16.3.1.
Business Interface and No-Interface View Method Exception
Handling
The following figure shows the exception handling for methods in the business interface or nointerface view of session beans:
299
16.3.2.
Web Service Client View and EJB 2.1 Client View Method
Exception Handling
The following figure shows the exception handling for methods in the web service client view and
methods in the EJB 2.1 client view:
Exception handling for web service methods and methods in the EJB 2.1 view of session beans.
300
16.3.3.
The following figure shows how system exceptions thrown by a PostConstruct or PreDestroy
method of a session bean are handled. These methods are not allowed to throw application
exceptions.
301
16.4.
302
16.5.
303
304
305
306
307
308
The three message-driven beans in the message destination link example program.
The example program is to be developed in an EJB Project in Eclipse, which is set up as described
in appendix B. All in all there are three classes in the project, which implement the three messagedriven beans. They are all located in the com.ivan.listeners package.
309
javax.annotation.PostConstruct;
javax.annotation.Resource;
javax.ejb.ActivationConfigProperty;
javax.ejb.MessageDriven;
javax.jms.Connection;
javax.jms.ConnectionFactory;
javax.jms.JMSException;
javax.jms.Message;
javax.jms.MessageListener;
javax.jms.MessageProducer;
javax.jms.Session;
javax.jms.Topic;
javax.naming.InitialContext;
/**
* Implements a listener facade bean that receives messages from a queue
* and passes them on to a topic.
*
* @author Ivan A Krizsan
*/
@MessageDriven(name = "ListenerFacadeMDB", mappedName = "jms/NewMessage",
activationConfig =
{
@ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Autoacknowledge"),
@ActivationConfigProperty(propertyName = "destinationType", propertyValue =
"javax.jms.Queue")
})
public class ListenerFacadeEJB implements MessageListener
{
/* Constant(s): */
private final static String PUBLISH_TOPIC_JNDI_NAME =
"java:comp/env/jms/PublishingTopic";
/* Instance variable(s): */
@Resource(mappedName="jms/NewMessageFactory")
private ConnectionFactory mJMSConnectionFactory;
private Topic mForwardingTopic;
/**
* Default constructor.
*/
public ListenerFacadeEJB()
{
System.out.println("********** ListenerFacadeEJB created");
}
@PostConstruct
public void initialize() throws Exception
{
/*
* This dependency, being a message destination reference declared
* in the deployment descriptor cannot be injected, but must be
* looked up this way.
*/
InitialContext theContext = new InitialContext();
mForwardingTopic = (Topic)theContext.lookup(PUBLISH_TOPIC_JNDI_NAME);
}
/**
* @see MessageListener#onMessage(Message)
*/
@Override
public void onMessage(Message inReceivedMessage)
{
MessageProducer theForwardingMsgProducer = null;
Connection theForwardingConnection = null;
310
try
{
/*
* To send a message to a queue or topic:
* - Create a connection using a JMS connection factory.
* In this case, the connection factory can create connections
* for both queues and topics.
* - Create a JMS session for outgoing messages.
* - Create a message producer, which is the object that is
* to send the message.
*/
theForwardingConnection = mJMSConnectionFactory.createConnection();
Session theForwardingSession =
theForwardingConnection.createSession(false,
Session.AUTO_ACKNOWLEDGE);
theForwardingMsgProducer =
theForwardingSession.createProducer(mForwardingTopic);
/*
* Don't need to create a new message, we just set a flag in the
* received message indicating that it is re-delivered.
*/
inReceivedMessage.setJMSRedelivered(true);
/* Send the message to the topic. */
theForwardingMsgProducer.send(inReceivedMessage);
} catch (final Exception theException)
{
theException.printStackTrace();
} finally
{
closeJMSResources(theForwardingConnection);
}
System.out.println("********** ListenerFacaceEJB - Forwarded message");
}
/**
* Closes the supplied JMS connection if it is not null.
* If supplied connection is null, then do nothing.
*
* @param inJMSConnection JMS connection to close.
*/
private void closeJmsResources(Connection inJMSConnection)
{
if (inJMSConnection != null)
{
try
{
inJMSConnection.close();
} catch (JMSException theException)
{
// Ignore exceptions.
}
}
}
}
Note that:
The reference to the topic to pass the messages on to must be looked up using its JNDI
name, as is done in the initialize method. It cannot be dependency-injected, because no
destination resource with the JNDI name exists in the application server.
311
javax.ejb.ActivationConfigProperty;
javax.ejb.MessageDriven;
javax.jms.Message;
javax.jms.MessageListener;
javax.jms.ObjectMessage;
/**
* This class implements a message driven EJB that listens for messages
* on a topic.
* Note that the @MessageDriven annotation contains an element named
* mappedName, which must be present and must have a JNDI name that
* exists in the application server (GlassFish v3).
* This is, however, not the JNDI name that the EJB will listen on.
* This JNDI name will be overridden by the <message-destination-link>
* in the deployment descriptor.
*
* @author Ivan A Krizsan
*/
@MessageDriven(name="ListenerMDB1", mappedName = "jms/StockInfo", activationConfig =
{
@ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Autoacknowledge"),
@ActivationConfigProperty(propertyName = "destinationType", propertyValue =
"javax.jms.Topic")
})
public class FirstListenerEJB implements MessageListener
{
/**
* Default constructor.
*/
public FirstListenerEJB()
{
System.out.println("********** FirstListenerEJB created");
}
/**
* Delivers a message to the message driven bean.
*
* @param inMessage Message to be delivered.
*/
@Override
public void onMessage(final Message inMessage)
{
System.out.println("********** FirstListenerEJB onMessage start");
if (inMessage instanceof ObjectMessage)
{
ObjectMessage theObjMsg = (ObjectMessage)inMessage;
System.out.println("
Object message: " + theObjMsg);
} else
{
System.out.println("
Not object message.");
}
System.out.println("********** FirstListenerEJB onMessage end");
}
}
312
Note that:
The optional element mappedName in the @MessageDriven annotation must have a value
that is the JNDI name of an existing destination resource in the application server (GlassFish
v3, in my case perhaps this is a peculiarity of GlassFish), otherwise we won't be able to
deploy the EJB to the server. This JNDI name can be any valid JNDI name, though, since
this is not the JNDI name of the queue the EJB will listen for messages on the <messagedestination-link> in the ejb-jar.xml deployment descriptor overrides this configuration.
javax.ejb.ActivationConfigProperty;
javax.ejb.MessageDriven;
javax.jms.Message;
javax.jms.MessageListener;
javax.jms.ObjectMessage;
/**
* This class implements a message driven EJB that listens for messages
* on a topic.
* Note that the @MessageDriven annotation contains an element named
* mappedName, which must be present and must have a JNDI name that
* exists in the application server (GlassFish v3).
* This is, however, not the JNDI name that the EJB will listen on.
* This JNDI name will be overridden by the <message-destination-link>
* in the deployment descriptor.
*/
@MessageDriven(name="ListenerMDB2", mappedName = "jms/StockInfo", activationConfig =
{
@ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Autoacknowledge"),
@ActivationConfigProperty(propertyName = "destinationType", propertyValue =
"javax.jms.Topic")
})
public class SecondListenerEJB implements MessageListener
{
/**
* Default constructor.
*/
public SecondListenerEJB()
{
System.out.println("********** SecondListenerEJB created");
}
/**
* Delivers a message to the message driven bean.
*
* @param inMessage Message to be delivered.
*/
@Override
public void onMessage(final Message inMessage)
{
System.out.println("********** SecondListenerEJB onMessage start");
if (inMessage instanceof ObjectMessage)
{
ObjectMessage theObjMsg = (ObjectMessage)inMessage;
System.out.println("
Object message: " + theObjMsg);
} else
{
System.out.println("
Not object message.");
}
System.out.println("********** SecondListenerEJB onMessage end");
}
}
313
A JNDI name referencing a message destination to which messages will be produced to.
This JNDI name is the JNDI name that is used in the EJB when programmatically looking
up the destination resource to which to publish the messages.
This JNDI name does not have to correspond to a destination resource in the application
server.
Optionally, we can also specify the destination type, that is, whether the producer publishes
messages to a queue or a topic.
We can also, optionally, specify whether the EJB produces to, consumes from or both
produces and consumes from the destination resource.
In the deployment descriptor configuration of the consumer(s), the message driven bean(s), only
two discrete elements are used in connection to message linking:
The <message-destination-link> element, which is required, again acts as an alias for a JNDI
name and specifies the JNDI name used to look up the reference to the destination resource
the message-driven bean is listening on.
Specifying a <message-destination-link> in a MDB overrides any annotation-based
configuration.
Finally, lets take a look at the finished ejb-jar.xml deployment descriptor, which is to be located in
the META-INF directory in the root of the source-path of the project:
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:ejb="http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd"
xsi:schemaLocation="
http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd"
version="3.1">
<enterprise-beans>
<message-driven>
<ejb-name>ListenerFacadeMDB</ejb-name>
<message-destination-link>ListenerMsgDest</message-destination-link>
<message-destination-ref>
<!-JNDI name used in the EJB to look up the destination
reference. Note that this name does not have to correspond
to a destination in the application server if a message
destination link is used.
When a message destination link is used, this reference
has to be looked up programmatically or being injected
from the deployment descriptor and cannot be
injected using annotations.
314
-->
<message-destination-ref-name>jms/PublishingTopic</message-destinationref-name>
<!-Here, the type of the destination which is referenced
can be specified using a <message-destination-type>
element. We can also specify how the destination reference
is used; whether the EJB produces or consumes messages
using the reference.
Both these configurations are optional.
-->
<message-destination-type>javax.jms.Topic</message-destination-type>
<message-destination-usage>Produces</message-destination-usage>
<!-Message destination link enabling linking message
producers with message consumers within one and the
same application.
The <message-destination-link> element is defined in
the message producer.
-->
<message-destination-link>ListenerMsgDest</message-destination-link>
</message-destination-ref>
</message-driven>
<message-driven>
<ejb-name>ListenerMDB1</ejb-name>
<ejb-class>com.ivan.listeners.FirstListenerEJB</ejb-class>
<messaging-type>javax.jms.MessageListener</messaging-type>
<transaction-type>Container</transaction-type>
<message-destination-type>javax.jms.Topic</message-destination-type>
<message-destination-link>ListenerMsgDest</message-destination-link>
</message-driven>
</enterprise-beans>
<assembly-descriptor>
<!-This element is used to specify a link between a logical message
destination name, used in <message-destination-link> elements
above, and a physical JNDI name.
-->
<message-destination>
<!-- Logical name. -->
<message-destination-name>ListenerMsgDest</message-destination-name>
<!-JNDI name of a physical queue defined in the application
server
-->
<lookup-name>jms/ForwardingTopic</lookup-name>
</message-destination>
</assembly-descriptor>
</ejb-jar>
Note the last section of the deployment descriptor, the <assembly-descriptor> element with its
<message-destination> child element. It is there that the mapping between the logical message
destination link name and the physical JNDI name is done.
The advantage of using message destination links is that the JNDI name is used only at one single
place and exchanging it for another JNDI name becomes very easy.
This concludes the message destination link example program.
315
In the left pane, go to the Configuration -> Java Message Service -> Physical Destinations
node.
Configure the additional settings of the new JMS Physical Destination as desired.
If you do not have any special requirements, then use the default settings. It is possible to
change the settings of a physical destination later.
Click the Save button to finish creation of the new JMS Physical Destination.
316
In the left pane, go to Resources -> JMS Resources -> Connection Factories.
Click the Save button to finish creation of the new JMS Connection Factory.
317
In the left pane, go to Resources -> JMS Resources -> Destination Resources.
318
319
320
Having finished the first step of the JDBC Connection Pool creation, click the Next button.
In the text-field below the Datasource Classname popup-menu, enter the classname
org.apache.derby.jdbc.EmbeddedDataSource.
This datasource class allows us to use the embedded Derby database.
Optionally, check the Ping checkbox.
Scroll down to the Additional Properties section.
Delete all additional properties except for DatabaseName and ConnectionAttributes.
321
Set the DatabaseName property value to the desired name of your database.
I set it to MyNewDB, without the quotes.
The data of the database will be stored in a directory with the same name as the database in
the config directory in the GlassFish domain directory to which the application is deployed.
Set the ConnectionAttributes property value to ;create=true, again without the quotes.
Create a new property named URL and set its value to jdbc:derby: and append the name
of the database. In my case, the URL is jdbc:derby:/MyNewDB.
The remaining parameters are left at their default values.
Having finished configuring the connection pool, click the Finish button.
322
323
Basic Concepts
When dealing with EJB and web application security, there are a few concepts that will appear
frequently:
Credential
Information used to authenticate a principal.
A common type of credential is a password.
Group
A number of users with some common characteristics, such as access privileges.
For instance, users that are allowed to use a system but not allowed to administer it may
belong to a group called plainusers.
Principal
Connects an identity with a credential which can be used for authentication. For instance,
associates a user with a password.
Role
A concept used to assign access privileges in an application. Roles are used in the
application to specify different areas of the application that requires different privileges to
access. For instance, an EJB implementing account management may require only regular
user privileges to list the accounts, but may require privileges of an accountant to create and
remove accounts. The application may thus declare a regular-user role and an
accountant role. The roles are later mapped to application server users and/or user groups
using a server-specific deployment descriptor. In GlassFish v3 this is the sun-web.xml or the
sun-ejb-jar.xml deployment descriptors.
Security Realm
A security realm specifies an identity storage technique, like JDBC, LDAP or flat file, and
defines how to interact with the identity storage in order to store and retrieve user's
authentication and grouping information.
User
One individual user of the system that, having presented some kind of credentials, is allowed
to use the system in whole or part.
The figure on the next page shows the relation of some of the concepts listed above.
324
The relationships between roles, users, groups, security realms, principal and credentials.
There is only one single Security Realm in the application server the LDAP Realm.
Looking at its name, a qualified guess would be that it stores and retrieves user information
and credentials using LDAP.
The LDAP security realm contains two Groups; the RegularUsers Group and the
ImportantUsers Group.
325
In the tree on the left, navigate to the Configuration -> Security -> Realms -> file node.
326
Click the Manage Users button in the upper left corner of the Edit Realm frame.
There will be a list of user in the file realm, similar to this:
Enter the name of the user, the name of the groups the user belong to and the user's
password.
327
sun-application.xml
sun-web.xml
sun-ejb-jar.xml
The mapping described below shows how to map a single user and a group of users to roles in the
sun-web.xml deployment descriptor, but the principle is the same for all the different deployment
descriptors.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sun-web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Application Server 9.0
Servlet 2.5//EN" "http://www.sun.com/software/appserver/dtds/sun-web-app_2_5-0.dtd">
<sun-web-app error-url="">
<context-root>/EJBSecurityAnnotations</context-root>
<!-The application's role "superusers" is mapped to the application
server principal (user) "ivan".
This gives the user "ivan" defined in the application server
access to the parts of the application that requires the user
to be in the "superusers" role.
-->
<security-role-mapping>
<role-name>superusers</role-name>
<principal-name>ivan</principal-name>
</security-role-mapping>
<!-The application's role "plainusers" is mapped to the application
server's user group "plain-users"
This gives all the users defined in the application server that
belongs to the "plain-users" group access to the parts of the
application that requires the user to be in the "plainusers"
role.
-->
<security-role-mapping>
<role-name>plainusers</role-name>
<group-name>plain-users</group-name>
</security-role-mapping>
<class-loader delegate="true" />
<jsp-config>
<property name="keepgenerated" value="true"/>
</jsp-config>
</sun-web-app>
328
Note that:
Each <security-role-mapping> element allows the mapping of one or more principals (users)
and/or one or more user groups to a single role.
This is not shown in the example above, where only a single user or group is mapped to a
role.
A <role-name> element specifies the name of the role in the application to which the
following principals (users) and groups are to be mapped to.
Each <principal-name> element specifies the name of a user defined in a realm in the
GlassFish application server.
Each <group-name> element specifies the name of a user group defined in a realm in the
GlassFish application server.
The first mapping mapping the user ivan to the role superusers allows the user ivan
defined in the application server access (after having presented the appropriate credentials (a
password)) to the parts of the application that requires the user to be in the superusers role.
The second mapping mapping the user group plain-users to the role plainusers allows
all the users in the group plain-users defined in the application server access (again after
having presented the appropriate credentials) to the parts of the application that requires the
user to be in the plainusers role.
329
330
Entering Maven group and artifact id as part of enabling Maven dependency management.
331
In the Eclipse project properties, set the java folder created in the previous step to be a
source folder. The result should look like this:
The project settings with the /src/main/java folder assigned to be a source folder.
Also in the Eclipse project properties for the project, in the Java Compiler tab, disable
project specific settings.
It is assumed that the default Eclipse JRE is a Java 6 JRE.
Make sure that Java SE 6 libraries are included on the project classpath.
332
Open the project's pom.xml file and modify the contents so the final result is like in the
listing below.
<project
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.ivan.scbcd6</groupId>
<artifactId>JavaSEEJBApp</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<repositories>
<repository>
<id>download.java.net</id>
<name>Java.net Maven Repository</name>
<url>http://download.java.net/maven/glassfish</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.glassfish.extras</groupId>
<artifactId>glassfish-embedded-all</artifactId>
<version>3.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
Note that:
The group and artifact ids may vary, depending on your project setup.
In order for Maven to find the JAR containing the GlassFish embeddable container we need
to add a the Java.net repository.
333
package com.ivan.scbcd6.ejbs;
import java.util.Date;
import javax.ejb.Local;
import javax.ejb.Stateless;
@Local
@Stateless(name="MyStatelessSessionBean")
public class StatelessSessionBean
{
public String sayHello(String inName)
{
return "Hello " + inName + ", it is now " + new Date();
}
}
package com.ivan.scbcd6.client;
import
import
import
import
import
import
java.util.HashMap;
java.util.Map;
javax.ejb.embeddable.EJBContainer;
javax.naming.Context;
javax.naming.NamingException;
com.ivan.scbcd6.ejbs.StatelessSessionBean;
/**
* Java SE EJB client using the embeddable EJB 3.1 container.
* Based on an example by Arun Gupta.
*/
public class JavaSEEJBClientMain
{
/*
* EJB JNDI name.
* If we do not set the application name when creating the embeddable
* EJB container, the EJB will be given the following JNDI names,
* depending on where the application is run:
* In Eclipse: java:global/classes/MyStatelessSessionBean
* Standalone: java:global/[JAR file name]/MyStatelessSessionBean
*/
public final static String EJB_JNDI_NAME =
"java:global/JavaSEEJBApp/MyStatelessSessionBean";
public final static String APPLICATION_NAME = "JavaSEEJBApp";
public static void main(String[] args)
{
/*
* Creates an embeddable EJB container instance.
* Setting the application name gives us consistent JNDI names
* of EJBs, regardless of where the application is run.
*/
Map<String, Object> theEjbContainerProperties =
new HashMap<String, Object>();
334
theEjbContainerProperties.put(EJBContainer.APP_NAME, APPLICATION_NAME);
EJBContainer theEjbContainer = EJBContainer.createEJBContainer(
theEjbContainerProperties);
try
{
/*
* Retrieve the JNDI naming context from the embeddable
* EJB container.
*/
Context theJndiContext = theEjbContainer.getContext();
/*
* Retrieve a reference to the EJB from the JNDI context.
*/
StatelessSessionBean theEjb = (StatelessSessionBean)
theJndiContext.lookup(EJB_JNDI_NAME);
/* Call the EJB and print the result. */
String theMessage = theEjb.sayHello("Ivan");
System.out.println("Message from EJB: " + theMessage);
} catch (NamingException theException)
{
theException.printStackTrace();
} finally
{
/*
* Make sure the embeddable EJB container is properly
* shut down.
*/
theEjbContainer.close();
}
}
}
Note that:
When creating the embeddable EJB container, a map containing properties is enclosed.
In this map, we enclose a property that sets the application name.
See the API documentation of the EJBContainer class for further details!
The application name is set in order to have a consistent JNDI name of the EJB, regardless
of whether we run the embeddable EJB container in a unit test or in a standalone Java SE
application.
If the application name were not set, then the EJB's JNDI name would become:
In Eclipse: java:global/classes/MyStatelessSessionBean
Standalone: java:global/[JAR file name]/MyStatelessSessionBean
The embeddable EJB container needs to be explicitly shut down using the close() method.
If this is not done, the JVM in which the application or unit test is running will keep
executing even after our main class has finished executing.
335
Right-click in the class file editor or on the class in the package explorer and select Run As
-> Java Application.
Console output similar to the following should appear (some output has been omitted to
conserve space):
Note that:
336
337
Configure the Runnable JAR file export as in the picture below with libraries copied to a
folder next to the exported JAR. Note that you can choose any Export destination.
Configuring the JAR export creating a standalone application that uses the embeddable EJB container.
The result should be a JAR file with the name of your choice. In my case, it is JavaSEEJB.jar. Next
to the JAR file there should be a folder in which you should find the glassfish-embedded-all-3.0
JAR library.
338
Note that:
The portable JNDI names for the EJB MyStatelessSessionBean are the same as when the
example program was run in Eclipse, that is:
java:global/JavaSEEJBApp/MyStatelessSessionBean!
com.ivan.scbcd6.ejbs.StatelessSessionBean
and
java:global/JavaSEEJBApp/MyStatelessSessionBean
A message from the EJB is printed.
The embeddable EJB container, and also the process the example program runs in, is
properly shut down and the JVM terminates.
This concludes the Embeddable EJB Container Example.
339