Skip to main content

What's New in EJB 3.0

August 18, 2005

{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 "http://en.wikipedia.org/wiki/Plain_Old_Java_Object">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:

[prettify]
/* 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();
}
[/prettify]

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:

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

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:

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

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

[prettify]
/* 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(){}
}
[/prettify]

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:

[prettify]
public Object <methodName>(javax.ejb.InvocationContext)
    throws Exception
[/prettify]

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

[prettify]
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;
[/prettify]

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:

respond to my request for clarification, so leave it and if it's a problem,
we'l get talkbacks. ca -->

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

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

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

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:

[prettify]
@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;
    }
}
[/prettify]

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:

[prettify]
/* 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;
    }
}
[/prettify]

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:

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

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:

[prettify]
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
    }
}
[/prettify]

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.

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

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

width="1" height="1" border="0" alt=" " />
Krishna Srinivasan is a software engineer working in Chennai, India.
Related Topics >> EJB   |