EJB 3.1: Working with Interceptors

Exclusive offer: get 50% off this eBook here
EJB 3.1 Cookbook

EJB 3.1 Cookbook — Save 50%

Enterprise JavaBean 3.1 - Build real world EJB solutions with a collection of simple but incredibly effective recipes with this book and eBook

$32.99    $16.50
by Richard M. Reese | July 2011 | Cookbooks Enterprise Articles Java

The use of interceptors provides a way of adding functionality to a business method without modifying the business method itself. The added functionality is not intermeshed with the business logic resulting in a cleaner and easier to maintain application. In the previous article we saw how the interceptors provide a means of moving code that is not central to a business method outside of the method. Here, we learn how to use interceptors to handle a number of different concerns including security and transactions.

In this article by Richard Reese, author of EJB 3.1 Cookbook, we will cover:

  • Using interceptors to enforce security
  • Using interceptors to handle transactions
  • Using interceptors to handle application statistics
  • Using lifecycle methods in interceptors

 

EJB 3.1 Cookbook

EJB 3.1 Cookbook

Build real world EJB solutions with a collection of simple but incredibly effective recipes with this book and eBook

        Read more about this book      

(For more resources on EJB, see here.)

The recipes in this article are based largely around a conference registration application as developed in the first recipe of the previous article on Introduction to Interceptors. It will be necessary to create this application before the other recipes in this article can be demonstrated.

Using interceptors to enforce security

While security is an important aspect of many applications, the use of programmatic security can clutter up business logic. The use of declarative annotations has come a long way in making security easier to use and less intrusive. However, there are still times when programmatic security is necessary. When it is, then the use of interceptors can help remove the security code from the business logic.

Getting ready

The process for using an interceptor to enforce security involves:

  1. Configuring and enabling security for the application server
  2. Adding a @DeclareRoles to the target class and the interceptor class
  3. Creating a security interceptor

How to do it...

Configure the application to handle security as detailed in Configuring the server to handle security recipe. Add the @DeclareRoles("employee") to the RegistrationManager class.

Add a SecurityInterceptor class to the packt package. Inject a SessionContext object into the class. We will use this object to perform programmatic security. Also use the @DeclareRoles annotation.

Next, add an interceptor method, verifyAccess, to the class. Use the SessionContext object and its isCallerInRole method to determine if the user is in the "employee" role. If so, invoke the proceed method and display a message to that effect. Otherwise, throw an EJBAccessException.

@DeclareRoles("employee")
public class SecurityInterceptor {

@Resource
private SessionContext sessionContext;

@AroundInvoke
public Object verifyAccess(InvocationContext context) throws
Exception {
System.out.println("SecurityInterceptor: Invoking method: " +
context.getMethod().getName());
if (sessionContext.isCallerInRole("employee")) {
Object result = context.proceed();
System.out.println("SecurityInterceptor: Returned from method: "
+ context.getMethod().getName());
return result;
} else {
throw new EJBAccessException();
}
}
}

Execute the application. The user should be prompted for a username and password as shown in the following screenshot. Provide a user in the employee role.

EJB 3.1 tutorial on Interceptors

The application should execute to completion.

EJB 3.1 tutorial on Interceptors

Depending on the interceptors in place, you will console output similar to the following:

INFO: Default Interceptor: Invoking method: register

INFO: SimpleInterceptor entered: register

INFO: SecurityInterceptor: Invoking method: register

INFO: InternalMethod: Invoking method: register

INFO: register

INFO: Default Interceptor: Invoking method: create

INFO: Default Interceptor: Returned from method: create

INFO: InternalMethod: Returned from method: register

INFO: SecurityInterceptor: Returned from method: register

INFO: SimpleInterceptor exited: register

INFO: Default Interceptor: Returned from method: register

How it works...

The @DeclareRoles annotation was used to specify that users in the employee role are associated with the class. The isCallerInRole method checked to see if the current user is in the employee role. When the target method is called, if the user is authorized then the InterceptorContext's proceed method is executed. If the user is not authorized, then the target method is not invoked and an exception is thrown.

See also

 

EJB 3.1 Cookbook Enterprise JavaBean 3.1 - Build real world EJB solutions with a collection of simple but incredibly effective recipes with this book and eBook
Published: June 2011
eBook Price: $32.99
Book Price: $54.99
See more
Select your format and quantity:
        Read more about this book      

(For more resources on EJB, see here.)

Using interceptors to handle transactions

Transaction processing is used in many applications. However, the implementation of transactions can clutter up business logic. The use of declarative annotations can make transaction processing easier to use. But there are still times when programmatic transaction is necessary such as with long running transactions. When it is, interceptors can be useful.

Getting ready

The essential steps to use interceptors for handling transactions include:

  1. Creating a transaction interceptor
  2. Annotating the target class to use bean-managed transactions
  3. Annotating the target method with the transaction interceptor

Implement bean managed transactions is not covered here.

How to do it...

To illustrate the use of interceptors to effect transaction processing, add a TransactionInterceptor class to the packt package. Using dependency injection, inject a UserTransaction object.

Next, add a verifyAccess method using the @AroundInvoke annotation. Within the method, begin the transaction, invoke the proceed method and then commit the transaction.

public class TransactionInterceptor {

@Resource

private UserTransaction userTransaction;

@AroundInvoke
public Object verifyAccess(InvocationContext context) throws
Exception {
userTransaction.begin();
System.out.println("Beginning transaction");
Object result = context.proceed();
System.out.println("Committing the transaction");
userTransaction.commit();
return result;
}
}

In the RegistrationManager class, add the @TransactionManagement annotation at the class level and specify bean-managed transactions. Also add an EntityManager instance for your application.

@TransactionManagement(TransactionManagementType.BEAN)
public class RegistrationManager {
...
@PersistenceContext(unitName = "RegistrationApplication-ejbPU")
private EntityManager entityManager;

Next, add a method called bulkRegister. The intent of this method is to provide a way of adding multiple attendees all from the same company at one time. It is passed an array of names and titles along with the company name. It then iterates through the arrays adding one attendee at a time. Add the @Interceptor annotation to the method using the transaction interceptor.

@Interceptors({TransactionInterceptor.class})
public void bulkRegister(String names[], String titles[],
String company) {
for(int i=0; i<names.length; i++) {
attendeeFacade.create(new Attendee(names[i],
titles[i],company));
}
}

Modify the RegistrationServlet to test the interceptor. Add two arrays for the names and titles along with a company variable. Initialize the arrays then invoke the bulkRegister method.

String names[] = {"John", "Paul", "Karen"};
String titles[] = {"Lead", "Programmer", "Adminsitrator"};
String company = "Acme Software";
registrationManager.bulkRegister(names, titles, company);

Execute the servlet. Depending on which interceptors are in place for the application, you should see an output sequence reflecting this registration process.

INFO: Beginning transaction

INFO: InternalMethod: Invoking method: bulkRegister

INFO: Default Interceptor: Invoking method: create

INFO: Default Interceptor: Returned from method: create

INFO: Default Interceptor: Invoking method: create

INFO: Default Interceptor: Returned from method: create

INFO: Default Interceptor: Invoking method: create

INFO: Default Interceptor: Returned from method: create

INFO: InternalMethod: Returned from method: bulkRegister

INFO: Committing the transaction

How it works...

When the bulkRegister method was called, the verifyAccess method of the TransactionInterceptor class was called first. The method started a transaction using the begin method and then invoked the target method, bulkRegister, with the proceed method. When this method returned, the transaction was committed using the commit method.

There's more...

Transaction processing can be more complex than illustrated in the previous example. When a method is invoked, it may or may not be part of another transaction. Exceptions may be thrown which force the roll back of a transaction. These and potentially other issues must be taken into consideration when using an interceptor to handle transactions.

To illustrate the issues involved, the following table outlines the steps needed to handle container-based transactions based on the transaction attribute of the target method. Pre-processing refers to those activities which should be performed before the proceed method is executed. The post-processing activities are those which should be performed when the proceed method returns.

EJB 3.1 tutorial on Interceptors

EJB 3.1 Cookbook Enterprise JavaBean 3.1 - Build real world EJB solutions with a collection of simple but incredibly effective recipes with this book and eBook
Published: June 2011
eBook Price: $32.99
Book Price: $54.99
See more
Select your format and quantity:
        Read more about this book      

(For more resources on EJB, see here.)

Using interceptors to handle application statistics

The gathering of application statistics is a common requirement. It may be desirable to determine how many times a method is executed or how much time is spent in a method. This recipe illustrates collecting both of these types of statistics using a chain of interceptors.

Getting ready

The process for developing and using interceptors for an application's statistics is similar to previous techniques and include:

  1. Creating a class to maintain the application's statistics
  2. Creating interceptors to support the gathering of the statistics
  3. Using the @Interceptors annotation to designate a target method

In this recipe we will create two interceptors. The first will keep track of the number of times a method is executed and the second will keep track of the amount of time spent in a method. We will apply the interceptors against the RegistrationManager's register method. If we need to keep track of statistics for more than one method, then a more sophisticated version of the statistic class would need to be created.

How to do it...

In both of the interceptors we will be using an instance of the ApplicationStatistics class to record information about the application. Since only one instance of this class is needed at a time, we will develop it as a singleton. Add the class to the packt package along with three member variables:

  • instance – Used to implement the singleton pattern
  • count – Keeps track of how many times a method is executed
  • totalTime – Keeps track of the total amount of time spent in a method

To implement the singleton, add a private default constructor to the class. This means there are no constructors available to create an instance of the class. Add a static getInstance method that creates a single instance of the class and returns the instance.

public class ApplicationStatistics {
private static ApplicationStatistics instance;
private static int count;
private long totalTime;

public static ApplicationStatistics getInstance() {
if(instance == null) {
instance = new ApplicationStatistics();
}
return instance;
}
...
}

Add four methods to the class:

  • getCount – Returns the value of count
  • increment – Increments the count
  • increaseTotalTime – Adds a time to totalTime
  • getTotalTime – Returns totalTime

public int getCount() {
return count;
}

public void increment() {
this.count++;
}

public void increaseTotalTime(long time) {
totalTime += time;
}

public long getTotalTime() {
return this.totalTime;
}

Next, create two interceptors: HitCounterInterceptor and TimeInMethodInterceptor. Both of these interceptors will use the ApplicationStatistics class.

In the HitCounterInterceptor class, add an instance variable for ApplicationStatistics and a method called incrementCounter. In this method, obtain an instance of the ApplicationStatistics using its getInstance method and then invoke its increment method. Next, call the proceed method and then return the result of the method.

public class HitCounterInterceptor {

ApplicationStatistics applicationStatistics;

@AroundInvoke
public Object incrementCounter(InvocationContext context) throws
Exception {
System.out.println("HitCounterInterceptor - Starting");
applicationStatistics = ApplicationStatistics.getInstance();
applicationStatistics.increment();
Object result = context.proceed();
System.out.println("HitCounterInterceptor - Terminating");
return result;
}
}

In the TimeInMethodInterceptor class, add an instance variable for ApplicationStatistics and a method called recordTime. In this method, obtain an instance of the ApplicationStatistics using its getInstance method. We will use the System class' currentTimeMillis method to get a start and an ending time. The difference between these two times will be used as an argument to the ApplicationStatistics's increaseTotalTime method. As we did in the HitCounterInterceptor interceptor, call the proceed method and then return the result of the method.

public class TimeInMethodInterceptor {

ApplicationStatistics applicationStatistics;

@AroundInvoke
public Object recordTime(InvocationContext context) throws
Exception {
System.out.println("TimeInMethodInterceptor - Starting");
applicationStatistics = ApplicationStatistics.getInstance();
long startTime = System.currentTimeMillis();
Object result = context.proceed();
long endTime = System.currentTimeMillis();
applicationStatistics.increaseTotalTime(endTime-startTime);
System.out.println("TimeInMethodInterceptor - Terminating");
return result;
}
}

In the RegistrationManager class, add an interceptor annotation to the register method for the two interceptors.

@Interceptors({HitCounterInterceptor.class,
TimeInMethodInterceptor.class})
public Attendee register(String name,
String title, String company) {

Modify the RegistrationServlet to get an instance of the ApplicationStatistics class and then create an attendee. Display the number of attendees and time spent in the register method.

ApplicationStatistics applicationStatus =
ApplicationStatistics.getInstance();
...
Attendee attendee = registrationManager.register("Bill Schroder",
"Manager", "Acme Software");
out.println("<h3>" + attendee.getName() + " has been
registered</h3>");
out.println("<h3>Number of attendees: " +
applicationStatus.getCount() + "</h3>");
out.println("<h3>Total Time: " + applicationStatus.getTotalTime() +
"</h3>");

Execute the servlet. Its output will appear similar to the following screenshot:

EJB 3.1 tutorial on Interceptors

The server console should illustrate the execution order of the interceptors:

INFO: HitCounterInterceptor - Starting

INFO: TimeInMethodInterceptor - Starting

INFO: register

INFO: TimeInMethodInterceptor - Terminating

INFO: HitCounterInterceptor - Terminating

How it works...

Notice the @Interceptors annotation for the register method included both interceptors. This is an example of interceptor chaining which will be discussed in the next section. Also note, the use of the currentTimeMillis method may not be accurate enough for some applications.

There's more...

There are three other topics regarding interceptor chaining we need to address:

  • Using the getContextData method
  • Understanding interceptor chaining
  • Excluding interceptors

Using the getContextData method

The InvocationContext interface has a getContextData method that can be used to pass information between interceptors. This can be illustrated through simple modification of HitCounterInterceptor and TimeInMethodInterceptor.

A java.util.Map object is returned from the getContextData method. We can add data to the map in one interceptor and retrieve it in a later interceptor. In this example, we will pass the count value generated in the HitCounterInterceptor. While this can be retrieved easily from the ApplicationStatistics class, using this is a simple way of demonstrating the use of the map.

In the HitCounterInterceptor add these two lines of code before the invocation of the proceed method. This retrieves the map and then assigns the count value to the key "count".

Map<String,Object> data = context.getContextData();
data.put("count", applicationStatistics.getCount());

In the TimeInMethodInterceptor, add this code at the beginning of the recordTime method. The map is retrieved and the "count" element is returned.

Map<String,Object> data = context.getContextData();
System.out.println("ContextData count: " + data.get("count"));

When the application executes, you will see the value display in the console window.

INFO: HitCounterInterceptor

INFO: TimeInMethodInterceptor

INFO: ContextData count: 2

Understanding interceptor chaining

When multiple interceptors are used, understanding the order of execution of the interceptors can be important. The rules for determining the order of interceptor execution are:

  • The default interceptors, specified in the ejb.jar file, will be executed first
  • Interceptors are executed in the order in which they are declared
  • EJB level interceptors are executed before method level interceptors
  • Interceptors defined within the target class are executed last
  • The interceptors of the super class of an EJB or interceptor classes are executed before the derived classes are.

We can see this in the execution sequence for the interceptors of this recipe. Consider the use of the following interceptors for the RegistrationManager class:

// DefaultInterceptor declared in the ejb-jar.xml file

@Interceptors(SimpleInterceptor.class)
public class RegistrationManager {
...
@Interceptors({HitCounterInterceptor.class,
TimeInMethodInterceptor.class})
public Attendee register(String name, String title,
String company) {
...
@AroundInvoke
public Object internalMethod(InvocationContext context) throws
Exception{
...
}

The output sequence illustrates the execution order for the register method. Comments have been added to clarify the sequence.

INFO: Default Interceptor: Invoking method: register // Default interceptor

INFO: SimpleInterceptor entered: register // Defined at class level

INFO: HitCounterInterceptor // Method level – first in list

INFO: TimeInMethodInterceptor // Method level – second in list

INFO: internalMethod: Invoking method: register // Class interceptor

INFO: register

...

The use of super classes is not illustrated here; however any interceptors of the EJB super class or an interceptor super class are executed before the derived class interceptors are executed. A super class interceptor for a class level interceptor will be executed before the super class of a method level interceptor.

Excluding interceptors

Sometimes it may be desirable to ignore certain interceptors. There are two annotations whose use will exclude the annotated method from execution:

  • @ExcludeDefaultInterceptors
  • @ExcludeClassInterceptors

The @ExcludeDefaultInterceptors annotation is used at the class level and will exclude default interceptors. The @ExcludeClassInterceptors annotation is used at the method level and will exclude annotations declared at the class level.

Use these interceptors with the RegistrationManager class as outlined below to exclude the use of the DefaultInterceptor and SimpleInterceptor.

@Interceptors(SimpleInterceptor.class)
@ExcludeDefaultInterceptors
public class RegistrationManager {
...
@Interceptors({HitCounterInterceptor.class,
TimeInMethodInterceptor.class})
@ExcludeClassInterceptors
public Attendee register(String name, String title,
String company) {
...
@AroundInvoke
public Object internalMethod(InvocationContext context) throws
Exception{
...
}

The output reflects the use of only the HitCounterInterceptor, TimeInMethodInterceptor and internal interceptors.

INFO: HitCounterInterceptor

INFO: TimeInMethodInterceptor

INFO: internalMethod: Invoking method: register

INFO: register

...

Using lifecycle methods in interceptors

Methods marked with annotations such as @PreDestroy and @PostConstruct are lifecycle methods. They are invoked during various phases in the lifecycle of an EJB. The @PrePassivate and @PostActivate annotations are also life cycle methods for stateful session beans. Each EJB type has a different set of lifecycle events. These lifecycle methods can also be used in interceptors.

Getting ready

We will be reusing the SimpleInterceptor class as defined in the Defining and using interceptors recipe. In this recipe, we will add a @PostConstruct annotation to illustrate the incorporation of lifecycle methods.

How to do it...

Modify the SimpleInterceptor and add a constructed method annotated with @PostConstruct. In the method, use the println method to display a simple message indicating the execution of the method.

public class SimpleInterceptor {

@PostConstruct
private void constructed(InvocationContext invocationContext) {
System.out.println("SimpleInterceptor constructed: ");
}

@AroundInvoke
public Object simpleMethod(InvocationContext context) throws
Exception {
System.out.println("SimpleInterceptor entered: " +
context.getMethod().getName());
Object result = context.proceed();
System.out.println("SimpleInterceptor exited: " +
context.getMethod().getName());
return result;
}
}

Execute the servlet using the interceptor declared at the class level for the RegistrationManager class. The output will show the constructed method being executed before the interceptor's simpleMethod is executed.

INFO: SimpleInterceptor constructed:

INFO: SimpleInterceptor entered: register

How it works...

Since the SimpleInterceptor's constructed method executes when the instance is created, it will execute before its simpleMethod executes for a target. Adding lifecycle methods to an interceptor can enhance the utility of the interceptor.

A lifecycle method is annotated with one of several possible lifecycle annotations. These methods must return void and normally are passed void. However, if they are used in an interceptor, then they have an InvocationContext argument.

Summary

In this article we learned how to use interceptors to handle a number of different concerns including security and transactions


Further resources on this subject:


About the Author :


Richard M. Reese

Richard M. Reese holds a Ph.D. in Computer Science from Texas A & M University and is currently an Associate Professor in the Department of Engineering and Physics at Tarleton State University in Stephenville, Texas. In addition to his experience in academia, Richard has over 17 years experience in industry including operating system development at GTE Automatic Electric Labs and at Lockheed Martin in Fort Worth, Texas, where he supervised a tool development group and oversaw various research and development projects. Prior to his industrial experience he served four years in the United States Air Force.

Richard has been involved with Java since 1996 and is a certified Java SE 7 Associate Programmer. He has worked as consultant/instructor of software languages in private and public classes providing him with a variety of insight into industry applications. He has published numerous papers and has developed courseware for a variety of topics including advanced Java technologies. He has also written the EJB 3.1 Cookbook and Java 7 New Features Cookbook for Packt Publishing.

Books From Packt


Java EE 6 with GlassFish 3 Application Server
Java EE 6 with GlassFish 3 Application Server

NetBeans IDE 7 Cookbook
NetBeans IDE 7 Cookbook

EJB 3 Developer Guide
EJB 3 Developer Guide

Java EE 5 Development with NetBeans 6
Java EE 5 Development with NetBeans 6

GlassFish Security
GlassFish Security

GlassFish Administration
GlassFish Administration

JBoss AS 5 Development
JBoss AS 5 Development

EJB 3.0 Database Persistence with Oracle Fusion Middleware 11g
EJB 3.0 Database Persistence with Oracle Fusion Middleware 11g


Your rating: None Average: 4 (1 vote)
very good by
This article is very good, I use it.

Post new comment

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
M
x
r
t
z
j
Enter the code without spaces and pay attention to upper/lower case.
Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software