|
|
|||||||||||||||
by Krishna Srinivasan | |||||||||||||||
| |||||||||
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.
DeploymentDescriptorsEJB 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.
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.
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 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.
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.
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@RunAsAnnotations 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.
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.
Krishna Srinivasan is a software engineer working in Chennai, India.
View all java.net Articles.
|
|