The Source for Java Technology Collaboration
User: Password:
Register | Login help    

Search

Online Books:
java.net on MarkMail:


 E-mail  Print

What's New in EJB 3.0

Thu, 2005-08-18

{cs.r.title}




Contents
Using Annotations Instead of
DeploymentDescriptors
Callback Methods and Listener Classes
Interceptors
Dependency Injection
EntityBeans Made Easy
Security Annotations
Conclusion
Resources

This article looks at the various new features introduced in the Enterprise JavaBean (EJB) 3.0 specification and how they'll make developing EJBs easier. The main theme of the EJB 3.0 is ease of development, which is the purpose of the new features in this release.

Using Annotations Instead of DeploymentDescriptors

EJB 3.0 uses metadata annotations as an alternative to deployment descriptors. Annotations were introduced in J2SE 5.0 and are a key element in the EJB 3.0 simplification. In EJB 3.0, all Enterprise JavaBeans are Plain Old Java Objects (POJO), with proper annotations. So a developer marks up his/her Java code with annotations and the annotation processor creates the deployment descriptors at runtime. This mechanism allows the deployer to override the default configs so they can replace data sources, etc. Another enhancement here is that the code and annotations are in one file--the developer doesn't have to maintain multiple files for one bean. For example, the following code shows how to define a simple stateless session bean:


/* Example class for Stateless session */
@Stateless
public class MessageStoreBean implements MessageStore{
    public String getMessage(){
        return "Test Message";
    }
}

/* Example class for Business Interface */
@Remote
public interface MessageStore{
    public String getMessage();
}

The @Stateless annotation marks the MessageStoreBean as a stateless session bean. Furthermore, the @Remote annotation indicates that it's a business interface. But it is not mandatory to write Remote interfaces: we can use @Remote in the bean class itself to let the container generate the business interface, as in the following example:


@Stateless
@Remote
public class MessageStoreBean{
    public String getMessage(){
        return "Test Message";
    }
}

In the above example, the bean class doesn't implement the business interface, and uses @Remote instead. This makes development much easier than in previous versions. EJB 3.0 provides annotations for every type of metadata previously addressed by deployment descriptors, so no XML descriptor is needed and you can deploy your beans simply by deploying a plain old .jar into your application server.

This doesn't mean that XML has been completely removed; it's now optional. If we define both a deployment descriptor and annotations, the deployment descriptor overrides the annotations.

Callback Methods and Listener Classes

The EJB 2.1 spec required you to implement either the interface javax.ejb.SessionBean or javax.ejb.EntityBean. Methods like ejbCreate(), ejbPassivate(), and ejbActivate() were never used in your application and just cluttered up your code. Fortunately, they're not required in EJB 3.0.

In EJB 3.0, bean developers do not have to implement unnecessary callback methods and can instead designate any arbitrary method as a callback method to receive notifications for lifecycle events for a SessionBean or MessageDrivenBean (MDB). Callback methods can be indicated using callback annotations.

Also, we can design a callback listener class instead of writing callback methods in the bean class itself. The annotations used for callback methods are the same in both cases--only the method signatures are different. A callback method defined in a listener class must take a Object as a parameter, which is not needed when the callback is in the bean itself. Here's an example of putting a callback in the bean:


/* Callback method defined inside a bean class */
@Stateful
public class TestBean{
    private int var;
    public int method(){}
    @PreDestroy testMethod(){}
}

In the above code fragment, we are defining a callback method in the bean class itself. @PreDestroy is one of the callback annotations.


/* Callback method defined inside a Listener class*/
public class CustomListener{
    @PrePassivate public testMethod(Object obj){
        // Statements
    }
}

/* Adds callback listener to bean class */
@CallbackListener CustomListener
@stateful
public class TestBean{
    private int var;
    public void getPrice(){}
}

Here, we are writing a separate listener class for callback methods and have a small difference in method signatures. We must pass an Object parameter, because we're defining the callback listener in a separate class. A bean class adds the callback listener class by using a special callback annotation @CallbackListener.

Interceptors

The runtime services like transaction and security are applied to the bean objects at the method's invocation time. These services are often implemented as the interceptor methods managed by the container. However, EJB 3.0 allows developers to write the custom interceptor methods that are called before and after the bean method. It is useful to give the control to the developer for the actions like commit transaction, security check, etc. You can develop, reuse, and execute your own services. Or, you can re-implement the transaction and security services to override the container's default behaviors.

Interceptors offer fine-grained control over method invocation flow. They can be used on SessionBeans (stateful and stateless) and MessageDrivenBeans. They can be defined in the same bean class or in an external class. The interceptor's methods will be called before the actual bean class methods are called.

The following is the signature of the interceptor method:


public Object <methodName>(javax.ejb.InvocationContext)
    throws Exception

The interceptor method must take the InvocationContext object. This interface defines the following methods:


package javax.ejb;
public interface InvocationContext{
    public Object getBean();
    public Method getMethod();
    public Object[] getParameters();
    public void setParameters(Object params[]);
    public EJBContext getEJBContext();
    public Object proceed() throws Exception;

The @Interceptor annotation designates an interceptor defined on another class. If more than one external interceptor is needed, the @Interceptors is used instead.

The @AroundInvoke indicates the method acts as an interceptor. The interceptors are applied to all business methods for the bean. We can use the interceptor to change the value of bean method's parameter. The following code is an example of an interceptor defined in a bean class itself:


@StateLess
@Interceptor("TestInterceptor")
public class LoginBean{
    @AroundInvoke
    public Object testInterceptor(InvocationContext invContext)
        throws Exception{
            invContext.proceed();
    }
}

And here's what an interceptor looks like when defined in an external class:


public class TestInterceptor{
    @AroundInvoke
    public Object myInterceptor(InvocationContext invContext)
        throws Exception{
            invContext.proceed();
    }
}

Dependency Injection

Dependency injection is a term used to describe a separation between the implementation of an object and the construction of an object it depends upon. Instead of complicated XML ejb-refs or resource refs, you can use the @Inject annotation to set the value of a field or to call a setter method within your session bean with anything registered within JNDI. EJB 3.0 facilitates this feature by providing annotations to inject the dependencies into the bean class itself. Dependency annotation may be attached to the bean class, instance variables, or methods. The main reason for introducing @Inject is to avoid JNDI lookup to get the resources set the JNDI tree. Also another great effect of using @Inject is to allow a bean to be tested outside of the container.

Consider the following code:


@Stateful
public class LoginBean implements Login{
    @Inject private UserTransaction tx;
    @Inject private DataSource ds;

    private ValidateBean validate;

    @EJB(name = "validateLogin")
    public void setValidateLogin(ValidateBean validate){
        this.validate = validate;
    }
}

When an instance of LoginBean is created, the EJB container will look for the values of tx and ds in the JNDI tree to assign the instance variables. Also, the container will get the reference for the EJB ValidateBean and call the setValidateLogin() setter method.

EntityBeans Made Easy

To create an EntityBean, a developer only needs to code a bean class and annotate it with appropriate metadata annotations. The bean class is a POJO.

Look at the following example code:


/* Example program for EntityBeans using annotations */
@Entity
public class TestEntityBean{
    private String userId;
    private String name;

    @id(generate=AUTO)
    public String getUserId(){
        return this.userId;
    }
    public void setUserId(String userId){
        this.userId = userId;
    }
    public String getName(){
        return this.name;
    }
    public void setName(String name){
        this.name = name;
    }
}

The @Entity annotation indicates the class is an EntityBean. Unlike in previous versions, in EJB 3.0 EntityBeans are not required to have Home interfaces, Business interfaces, and deployment descriptors. In EJB 3.0, an EntityBean is a concrete class, not a abstract class, as was the case in EJB 2.1. Of course, this means that methods are concrete and not abstract. We can create an instance of EntityBean using the new() operator like normal.

Security Annotations

EJB 3.0 provides annotations to specify security options. The following are the security-related annotations defined in EJB 3.0:

  • @SecurityRoles
  • @MethodPermissions
  • @Unchecked
  • @Exclude
  • @RunAs

Annotations applied for package-level elements are called package-level annotations. These annotations are placed in the file package-info.java. The security roles are applied to the entire EJB module. The @SecurityRoles annotation must be placed in the package-info.java file with the package information. When the compiler parses package-info.java, it will create a synthetic interface. It does not have any source code, because it is created by the compiler. This interface makes package-level annotations available at runtime. The file package-info.java is created and stored inside of every package. For example, if your bean class is inside of the package ejb3.login, then you must put your package-info.java file inside the ejb3.login package with the user role details.

The package-info.java file is new in J2SE 5.0. It contain package declaration, annotations, package tags and Javadoc tags. It is preferred over the package.html file used in the previous versions, because package.html can contain only package comments and Javadocs, not annotations. A package may contain either package-info.java or package.html, but not both. Place either file in the package directory in the source tree along with your .java files.

The following example shows how to define a @SecurityRoles annotation:


@javax,ejb.SecurityRoles(roleNames={"admin",user"})
package oreilly.ejb30;

In the above code, we defined two role names: admin and user. We have to use the @MethodPermissions annotation to specify the method permissions for each method, as in the following example:


import javax.ejb.MethodPermissions;
import javax.ejb.StateLess;
import javax.ejb.Unchecked;
@Stateless
@MethodPermissions("admin")
public class LoginBean{
    public void updateProfile(){
        System.out.println("Prifile Updated");
    }
    @MethodPermissions("GUEST")
    public void login(){
        //Statements
    }
    @Unchecked
    public void homePage(){
        //Statements
    }
}

If we put a @MethodPermisions above the class declaration, it is considered the default security role for all methods in the bean class. A @MethodPermission defined for a specific method will include the default security role as well as whatever permissions it defines. In the above example, admin is the default security role for all the methods.

In this example, the updateProfile() method can be accessed only by the admin role. The login() method can be accessed by both admin and GUEST roles. The homePage() method has no restrictions, since @Unchecked implies that anyone can access that method.

Mapping these security roles to users or groups is done in the application-server-specific XML file. For example, if your are working on Oracle Application Server, you will configure these mappings in the orion-ejb-jar.xml file. See the following example.


      <assembly-descriptor>
        <security-role-mapping name="GUEST">
                <user name="user"/>
        </security-role-mapping>
      </assembly-descriptor>
  

Map logical roles defined in the annotations to actual users and groups defined in a user repository. The mapping is specified in the OC4J-specific deployment descriptor with a <security-role-mapping> element. In the above example, the logical role GUEST defined using annotations is mapped to the user named user.

Conclusion

The proposals made in EJB 3.0 drafts look very promising for enterprise Java developers. This should be a great time for EJB developers to work with the long-awaited new version. Now it's time for application server vendors to implement these specifications and provide a good option for enterprise application development.

Resources

Krishna Srinivasan is a software engineer working in Chennai, India.
Related Topics >> EJB      
Comments
Comments are listed in date ascending order (oldest first)