EJB 3.1: Introduction to Interceptors

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

EJB 3.1 Cookbook — Save 80%

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

₨1,016.00    ₨203.20
by Richard M. Reese | July 2011 | Cookbooks Enterprise Articles Java

Interceptors provide a way to achieve cross-cutting activities like logging, managing transactions, security, and other aspects of an application. This article by Richard Reese, author of EJB 3.1 Cookbook, explains how the interceptors provide a means of moving code that is not central to a business method outside of the method. Later we will learn how to use interceptors to handle a number of different concerns including security and transactions in the next article.

In this article, we will cover:

  • Creating the Registration Application
  • Defining and using interceptors
  • Using the InvocationContext to verify parameters

 

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.)

Introduction

Most applications have cross-cutting functions which must be performed. These cross-cutting functions may include logging, managing transactions, security, and other aspects of an application. Interceptors provide a way to achieve these cross-cutting activities.

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.

Aspect Oriented Programming (AOP) is concerned with providing support for these cross-cutting functions in a transparent fashion. While interceptors do not provide as much support as other AOP languages, they do offer a good level of support.

Interceptors can be:

  • Used to keep business logic separate from non-business related activities
  • Easily enabled/disabled
  • Provide consistent behavior across an application

Interceptors are specific methods invoked around a method or methods of a target EJB. We will use the term target, to refer to the class containing the method(s) an interceptor will be executing around.

The interceptor's method will be executed before the EJB's method is executed. When the interceptor method executes, it is passed as an InvocationContext object. This object provides information relating to the state of the interceptor and the target. Within the interceptor method, the InvocationContext's method proceed can be issued that will result in the target's business method being executed or, as we will see shortly, the next interceptor in the chain. When the business method returns, the interceptor continues execution. This permits execution of code before and after the execution of a business method.

Interceptors can be used with:

  • Stateless session EJBs
  • Stateful session EJBs
  • Singleton session EJBs
  • Message-driven beans

The @Interceptors annotation defines which interceptors will be executed for all or individual methods of a class. Interceptor classes use the same lifecycle of the EJB they are applied to, in the case of stateful EJBs, which means the interceptor could be passivated and activated. In addition, they support the use of dependency injection. The injection is done using the EJB's naming context.

More than one interceptor can be used at a time. The sequence of interceptor execution is referred to as an interceptor chain. For example, an application may need to start a transaction based on the privileges of a user. These actions should also be logged. An interceptor can be defined for each of these activities: validating the user, starting the transaction, and logging the event. The use of interceptor chaining is illustrated in the Using interceptors to handle application statistics recipe.

Lifecycle callbacks such as @PreDestroy and @PostConstruct can also be used within interceptors. They can access interceptor state information as discussed in the Using lifecycle methods in interceptors recipe.

Interceptors are useful for:

  • Validating parameters and potentially changing them before they are sent to a method
  • Performing security checks
  • Performing logging
  • Performing profiling
  • Gathering statistics

An example of parameter validation can be found in the Using the InvocationContext to verify parameters recipe. Security checks are illustrated in the Using interceptors to enforce security recipe. The use of interceptor chaining to record a method's hit count and the time spent in the method is discussed in the Using interceptors to handle application statistics recipe. Interceptors can also be used in conjunction with timer services.

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

Creating the Registration Application

A RegistrationApplication is developed in this recipe. It provides the ability of attendees to register for a conference. The application will record their personal information using an entity and other supporting EJBs. This recipe details how to create this application.

Getting ready

The RegistrationApplication consists of the following classes:

  • Attendee – An entity representing a person attending the conference
  • AbstractFacade – A facade-based class
  • AttendeeFacade – The facade class for the Attendee class
  • RegistrationManager – Used to control the registration process
  • RegistrationServlet – The GUI interface for the application

The steps used to create this application include:

  1. Creating the Attendee entity and its supporting classes
  2. Creating a RegistrationManager EJB to control the registration process
  3. Creating a RegistrationServlet to drive the application

The RegistrationManager will be the primary vehicle for the demonstration of interceptors.

How to do it...

Create a Java EE application called RegistrationApplication. Add a packt package to the EJB module and a servlet package in the application's WAR module.

Next, add an Attendee entity to the packt package. This entity possesses four fields: name, title, company, and id. The id field should be auto generated. Add getters and setters for the fields. Also add a default constructor and a three argument constructor for the first three fields. The major components of the class are shown below without the getters and setters.

@Entity
public class Attendee implements Serializable {
private String name;
private String title;
private String company;
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

public Attendee() {
}

public Attendee(String name, String title, String company) {
this.name = name;
this.title = title;
this.company = company;
}
}

Next, add an AttendeeFacade stateless session bean which is derived from the AbstractFacade class. The AbstractFacade class is not shown here.

@Stateless
public class AttendeeFacade extends AbstractFacade<Attendee> {
@PersistenceContext(unitName = "RegistrationApplication-ejbPU")
private EntityManager em;

protected EntityManager getEntityManager() {
return em;
}

public AttendeeFacade() {
super(Attendee.class);
}
}

Add a RegistrationManager stateful session bean to the packt package. Add a single method, register, to the class. The method should be passed three strings for the name, title, and company of the attendee. It should return an Attendee reference. Use dependency injection to add a reference to the AttendeeFacade. In the register method, create a new Attendee and then use the AttendeeFacade class to create it. Next, return a reference to the Attendee.

@Stateful
public class RegistrationManager {

@EJB
AttendeeFacade attendeeFacade;

Attendee attendee;

public Attendee register(String name, String title,
String company) {
attendee = new Attendee(name, title, company);
attendeeFacade.create(attendee);
return attendee;
}
}

In the servlet package of the WAR module, add a servlet called RegistrationServlet. Use dependency injection to add a reference to the RegistrationManager. In the try block of the processRequest method, use the register method to register an attendee and then display the attendee's name.

public class RegistrationServlet extends HttpServlet {
@EJB
RegistrationManager registrationManager;

protected void processRequest(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();

try {

out.println("<html>");
out.println("<head>");
out.println("<title>Servlet RegistrationServlet</title>");
out.println("</head>");
out.println("<body>");
Attendee attendee = registrationManager.register("Bill
Schroder", "Manager", "Acme Software");
out.println("<h3>" + attendee.getName() + " has been
registered</h3>");
out.println("</body>");
out.println("</html>");
} finally {
out.close();
}
}
...
}

Execute the servlet. The output should appear as shown in the following screenshot:

EJB 3.1 tutorial on Interceptors

How it works...

The Attendee entity holds the registration information for each participant. The RegistrationManager session bean only has a single method at this time. In later recipes we will augment this class to add other capabilities. The RegistrationServlet is the client for the EJBs.

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: ₨1,016.00
Book Price: ₨1,694.00
See more
Select your format and quantity:
        Read more about this book      

(For more resources on EJB, see here.)

Defining and using interceptors

In this recipe, we will examine the process of creating and using a simple interceptor. An interceptor method will be invoked before a target method is executed. When the target method returns, additional code within the interceptor can be executed. This approach is useful for performing orthogonal tasks and for manipulating the parameters sent to a method and/or returned from a method.

Getting ready

There are two major steps in the creation and use of an interceptor:

  1. Creating a class containing an @AroundInvoke annotated method
  2. Specifying the target method using the @Interceptors annotation

To use an interceptor, we must first create it and then specify where it will be used. The @AroundInvoke annotation designates a method as an interceptor method. The method that follows must have a specific signature as shown here:

public Object methodName(InvocationContext context) throws Exception

The interceptor method can be declared either within the target class or in a separate class.

How to do it...

Add a class called SimpleInterceptor to the packt package. Within this class add a method called simpleMethod which is preceded with the @AroundInvoke annotation. Within the method display the name of the target method, invoke the target method with the proceed method and then display the target method name again as shown here:

public class SimpleInterceptor {
@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;
}
}

The second step is to indicate where the interceptor is to be used. It can be applied in one of three places. Here it is used at the class level using the @Interceptors annotation. Add an @Interceptors annotation to the RegistrationManager class. Notice the annotation takes the interceptor class as its argument. In this example, only the business methods of the RegistrationManager class will be intercepted and handled by the SimpleInterceptor.

@Stateful
@Interceptors(SimpleInterceptor.class)
public class RegistrationManager {
...
}

Execute the servlet. It should behave the same way it did earlier but you should notice the following messages being displayed in the server or development environment console window:

INFO SimpleInterceptor entered: register

INFO: register

INFO: SimpleInterceptor exited: register

How it works...

The interceptor will be executed before the EJB's method is executed. The sequence of actions an interceptor will take depends on the specific interceptor. However, this list details possible actions:

  • Perform any processing needed before the target method is invoked (possibly modifying the target method parameters)
  • Invoke the target using the proceed method
  • Perform post target processing (possibly modifying the target method's return value)
  • If no exceptions have been thrown, return the target method's result

The interceptor is responsible for invoking the target. If the interceptor does not invoke the target, then it must still return a result or throw an exception. The InvocationContext's proceed method invokes the target method. When the interceptor returns, it will normally use the return value of the target method. The target method can be invoked zero or more times.

In the method, simpleMethod, an InvocationContext object was passed that contains information regarding the invocation such as the name of the target method. This object's getMethod returns another object representing the target method whose getName method will return the target method's name. We used this technique twice: once before the proceed method was called and once after the target method returned. Notice the proceed method returned a value we stored in result which was then returned by the interceptor.

Interceptors can be defined either:

  • Inside of the class where they will be used, or
  • In an interceptor class

Binding an interceptor to a class or a method can be done using either annotations or a deployment descriptor.

There's more...

Interceptors can be created in three different ways:

  1. For all EJBs in an EJB module
  2. For all of the methods of a class
  3. For a specific method

Interceptors can also be declared within the target class itself. In addition, more than one interceptor class can be specified in the @Interceptors annotation. Here we have three interceptors defined for use with the register method.

@Interceptors({LogInterceptor.class, SecurityInterceptor.class,
TransactionInterceptor.class})
public Attendee register(String name, String title, String company) {
...
}

These interceptors will be executed in the order they appear in the annotation.

Creating an Interceptor for all EJBs in an EJB module

We can associate an interceptor with all of the EJBs of an application by adding an <interceptor-binding> element to the application's EJB jar. Java EE 6 application no longer requires deployment descriptors as annotations have largely taken their place. However, there are certain situations where they are still needed. One of these situations is when we want to use a default interceptor.

You will probably have to create an ejb-jar.xml file for your application. This process is development-environment specific. For example, in NetBeans, right-click on the EJB module for your project in the Project Explorer and select New ... Standard Deployment Descriptor. This will create the ejb-jar.xml file.

The process of associating an interceptor with all of the EJBs of an application involves:

  1. Adding an <interceptors> element to define the interceptor
  2. Adding an <assembly-descriptor> element to associate the interceptor with all of the methods of the application
  3. Creating the interceptor class

Once you have created the ejb-jar.xml, add an <interceptors> element to declare your default interceptor. Within the element add one <interceptor> element for each interceptor. In this case, there is only one. The <interceptor-class> element is needed to specify the name of the interceptor class.

<interceptors>
<interceptor>
<interceptor-class>packt.DefaultInterceptor</interceptor-class>
</interceptor>
</interceptors>

Next, add an <interceptor-binding> element inside of an <assembly-descriptor> element to bind the interceptor to all of the EJBS in the module. The <ejb-name> element uses an asterisk to specify all EJBs in the module. The <interceptor-class> element specifies the name of the interceptor.

<assembly-descriptor>
<interceptor-binding>
<ejb-name>*</ejb-name>
<interceptor-class>packt.DefaultInterceptor</interceptor-class>
</interceptor-binding>
</assembly-descriptor>

One possible version of the ejb-jar.xml file follows:

<?xml version="1.0" encoding="UTF-8"?>

<ejb-jar xmlns = "http://java.sun.com/xml/ns/javaee"
version = "3.1"
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">
<interceptors>
<interceptor>
<interceptor-class>packt.DefaultInterceptor</interceptor-class>
</interceptor>
</interceptors>
<assembly-descriptor>
<interceptor-binding>
<ejb-name>*</ejb-name>
<interceptor-class>packt.DefaultInterceptor</interceptor-class>
</interceptor-binding>
</assembly-descriptor>
</ejb-jar>

We need to create the default interceptor before we can run our application. Add a new class to the packt package called DefaultInterceptor. It should be very similar to the SimpleInterceptor except for the use of the method name defaultMethod and there will be no need to use the @Interceptors annotation at the class level. Add println methods to indicate we are executing from the default interceptor.

public class DefaultInterceptor {
@AroundInvoke
public Object defaultMethod(InvocationContext context) throws
Exception{
System.out.println("Default Interceptor: Invoking method: " +
context.getMethod().getName());
Object result = context.proceed();
System.out.println("Default Interceptor: Returned from method: "
+ context.getMethod().getName());
return result;
}
}

Execute the application. Once again we should not see any difference in the output of the servlet. However, the output in the console will reflect the execution of both the SimpleInterceptor and the DefaultInterceptor as shown here:

INFO: Default Interceptor: Invoking method: register

INFO SimpleInterceptor entered: register

INFO: register

INFO: Default Interceptor: Invoking method: create

INFO: Default Interceptor: Returned from method: create

INFO: SimpleInterceptor exited: register

INFO: Default Interceptor: Returned from method: register

Notice the DefaultInterceptor begins execution before the SimpleInterceptor and completes after it. This is an example of an interceptor chain. One interceptor is executed followed by another and then by the target method. The order of interceptor chaining is detailed in the Using interceptors to handle application statistics recipe.

Using XML notation means we can change the interceptor used without recompiling the source code. This is of particular value if the interceptor is concerned with "system" type functions such as counting the number of times an application is accessed.

Creating an interceptor for all methods of a class

If the intent is to use the same interceptor for all methods of an EJB, then we simply apply the @Interceptors annotation to the class as we did with the SimpleInterceptor.

@Stateful
@Interceptors({SimpleInterceptor.class})
public class RegistrationManager {

If we don't want a class level interceptor to be executed for a particular method, we can use the @ExcludeClassInterceptors annotation with the method. This annotation has no arguments and will not use any interceptors declared at the class level.

@ExcludeClassInterceptors
public Attendee register(String name, String title,
String company) {
...
}

Execute the application using this annotation. The output should be the same except you should not see the SimpleInterceptor being used with the register method.

Creating an interceptor for a specific method

Interceptors can also be declared at the method level. The @Interceptors annotation is used the same way but precedes the method declaration instead of the class declaration. The basic steps include:

  1. Defining an interceptor to use
  2. Annotating the method with the @Interceptors annotation

To illustrate this type of interceptor, add a new interceptor class called, MethodInterceptor to the packt package. It will be very similar to the previous interceptors except it uses a method name of methodLevel. Add println methods to indicate we are executing from this interceptor.

public class MethodInterceptor {
@AroundInvoke
public Object methodLevel(InvocationContext context) throws
Exception{
System.out.println("Method Interceptor: Invoking method: " +
context.getMethod().getName());
Object result = context.proceed();
System.out.println("Method Interceptor: Returned from method: " +
context.getMethod().getName());
return result;
}
}

Add the @Interceptors annotation to the RegistrationManager's register method specifying the MethodInterceptor class.

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

Execute the servlet. If the DefaultInterceptor and SimpleInterceptor are still being used, then you will see all three executing.

INFO: Default Interceptor: Invoking method: register

INFO: SimpleInterceptor entered: register

INFO: Method Interceptor: Invoking method: register

INFO: register

INFO: Default Interceptor: Invoking method: create

INFO: Default Interceptor: Returned from method: create

INFO: Method Interceptor: Returned from method: register

INFO: SimpleInterceptor exited: register

INFO: Default Interceptor: Returned from method: register

Declaring an interceptor in the target class

An interceptor method can be declared within a target class in the same way as in a separate class using the @AroundInvoke annotation. The interceptor will be applied to all methods of the EJB unless specified otherwise.

Add a new method to the RegistrationManager class called, internalMethod. Place the @AroundInvoke annotation in front of it. The method should have the same structure as previous interceptors but with the new method name and different println methods.

@AroundInvoke

public Object internalMethod(InvocationContext context) throws
Exception{
System.out.println("internalMethod: Invoking method: " +
context.getMethod().getName());
Object result = context.proceed();
System.out.println("internalMethod: Returned from method: " +
context.getMethod().getName());
return result;
}

Execute the servlet. Assuming the previous interceptors are still in place, your output should be as follows:

INFO: Default Interceptor: Invoking method: register

INFO: SimpleInterceptor entered: register

INFO: Method Interceptor: 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: Method Interceptor: Returned from method: register

INFO: SimpleInterceptor exited: register

INFO: Default Interceptor: Returned from method: register

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: ₨1,016.00
Book Price: ₨1,694.00
See more
Select your format and quantity:
        Read more about this book      

(For more resources on EJB, see here.)

Using the InvocationContext to verify parameters

The InvocationContext interface is used to support information handling within and between interceptors. It is passed as the single argument to a method annotated with the @AroundInvoke annotation. It possesses several methods which can assist in the handling of interceptors. In this recipe we will focus on those methods which provide access to the target method's parameter list and use them to manipulate the parameters.

Getting ready

The basic approach for using an interceptor to validate a target method's parameters involves:

  1. Accessing the target's parameters using the getParameters method
  2. Validating and possibly modifying the parameters
  3. Using the setParameters method to apply any changes to the parameters
  4. Invoking the proceed method

How to do it...

To keep the example simple, we will perform a simple validation. We will call the RegistrationManager's register method and make sure the parameters do not have any leading or trailing blanks.

First, add a new interceptor to the packt package called ValidationInterceptor. Call its method validateParameters as shown below. In this method we use the println method to display the execution sequence of the interceptor and the parameters as they are modified.

public class ValidationInterceptor {
@AroundInvoke
public Object validateParameters(InvocationContext context) throws
Exception{
System.out.println("ValidationInterceptor");

Object parameters[] = context.getParameters();
for(int i=0; i<parameters.length; i++) {
System.out.println("Before: ["+(String)parameters[i] + "]");
parameters[i] = ((String)parameters[i]).trim();
System.out.println("After: ["+(String)parameters[i]+"]");
}
context.setParameters(parameters);

Object result = context.proceed();
return result;
}
}

Next, precede the RegistrationManager's register method with the interceptor annotation.

@Interceptors(ValidationInterceptor.class)

In addition, change the output of the RegistrationServlet to create an attendee that has a combination of leading and trailing blanks for its arguments.

Attendee attendee = registrationManager.register(" Bill Schroder ",
"Manager", "Acme Software");
out.println("<h3>" + attendee.getName() + " has been
registered</h3>");

Execute the servlet and examine the console output. Depending on the interceptors in place, your output should be similar to the following:

INFO: ValidationInterceptor

INFO: Before: [ Bill Schroder ]

INFO: After: [Bill Schroder]

INFO: Before: [Manager ]

INFO: After: [Manager]

INFO: Before: [ Acme Software]

INFO: After: [Acme Software]

How it works...

When the interceptor is first executed we used the getParameters method to retrieve the parameters passed to the target method. The String method trim was used to remove any leading or trailing blanks for each parameter. The setParameters method was then used to pass these new values to the target method. The target method was then invoked with the proceed method. Additional println methods have be included to help illustrate the execution sequence.

The InvocationContext interface provides information about the state of a chain of interceptors and the target. During the execution of this invocation chain, it can be useful to maintain state information. The InvocationContext interface has seven methods useful for this purpose as listed in the following table. The interceptor has complete access to the target method's name and its parameters.

EJB 3.1 tutorial on Interceptors

There's more...

The InvocationContext interface has a number of other useful methods. The getContextData method is used to pass information between interceptors. This is illustrated in the Using interceptors to handle application statistics recipe.

Here we will examine two other methods:

  • Using getTarget to return information about the target
  • Using getMethod to return information about the target's method

Using getTarget to return information about the target

The getTarget method returns a reference to the target. Add this statement to the beginning of the validateParameters method. It returns a reference to the RegistrationManager.

System.out.println("ValidationInterceptor");
System.out.println(context.getTarget());

One possible output appears as follows:

INFO: ValidationInterceptor

INFO: packt._RegistrationManager_Serializable@101b74e

This reference can be used to access fields and methods of the class. For example, to execute a method we could use code similar to the following:

((RegistrationManager)context.getTarget()).methodName();

Using getTarget to return information about the target's method

The getMethod returns a java.lang.reflect.Method object which is useful if you need more detailed information about the method such as the annotations used, exceptions thrown, and parameter types to mention a few.

For example, to determine which annotations are used with the target method, insert the following code at the beginning of the validateParameters method. The getAnnotations method returns an array of annotations used with the method. Using a for each statement and println method we can display this list.

System.out.println("ValidationInterceptor");
Annotation annotations[] = context.getMethod().getAnnotations();
for(Annotation annotation: annotations) {
System.out.println(annotation);
}

The output below shows only one annotation being used at this time.

INFO: ValidationInterceptor

INFO: @javax.interceptor.Interceptors(value=[class packt.ValidationInterceptor])

See the Reflection API (java.lang.reflect) to further explore the capabilities of the Method class.

Summary

This article explained how the interceptors provide a means of moving code that is not central to a business method outside of the method.

We will learn how to use interceptors to handle a number of different concerns including security and transactions in the next article.

 


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


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