Results 1 to 1 of 1
- 12-12-2011, 01:17 PM #1
Member
- Join Date
- Dec 2011
- Posts
- 64
- Rep Power
- 0
Tutorial: In Depth View of EJB Bean for Component Developer Exam
In our previous article, we did a tutorial of the Java Enterprise Edition 5 for Enterprise JavaBeans version 3 specification. In this article we will look more in depth in the EJB3 specification focusing on constructing a typical an EJB for solving a realistic problem using EJB 3. We will focus on high-level features and services of EJB 3, including the major actors: the beans and clients. It will be obvious to you that EJB 3 services can be delivered in a straight and easy manner.
Diving into a View of EJBs
In the previous tutorial, we showed the change from EJB2 to EJB 2. The key issues that it addressed in relation to EJB 2. A cumbersome programming model, deployment descriptor overload, and the requirement to use a Java Naming Directory Interface for accessing key resources. EJB3 completely revamped it’s programming model. Here are the key changes that have drastically simplified EJBs:
Dependency Injection with EJB3
Almost every nontrivial application is made up of two or more classes that collaborate with each other in order to perform some business logic. The conventional method for doing this was for each object to assume responsibility for obtaining its own references to the objects it collaborates with as well as its dependencies. This leads to tightly coupled code that is difficult to test, difficult to reuse, difficult to understand, and often leads to situations where when you fix one bug, it creates one or more new bugs. This is not to say that coupling is unnecessary certain amount of coupling is necessary as uncoupled code isn’t capable of doing anything useful. In order to do anything useful, classes need to know about each other somehow. Coupling is necessary, but must be carefully managed.
With dependency injection, objects are given their dependencies at creation time. This is done by some other object or container that coordinates each object in the system. The container reads target bean configuration, figures out what beans and resources the target bean needs, and injects them into the bean at runtime. There is no need to write lookup code and it is easy to change the configuration to swap out beans and resources as required. So you declare component dependencies and let the container deal with all the issues of service or resource instantiation, initialization, sequencing, and supplies the service or resource references to the clients as required. The dependencies of an object are injected into the objects that need them rather than expecting the object to create or obtain their dependencies themselves. The benefit of this is that component implementations can easily be swapped out as necessary simply by reconfiguring the application.
Dependencies Accessed using JNDI
For all resources that are outside of the EJB container, the only means of accessing them is via the Java Naming Directory Interface (JNDI). This applies as well to invoking a session bean from a client running outside of an EJB container. JNDI has a set of interfaces containing implementations for Remote Method Invocation (RMI), Lightweight Directory Access Protocol (LDAP), CORBA as well as others. In order to perform a JNDI lookup is to setup the JNDI properties in a java.util.Properties object and then pass this object as an argument to InitialContext. Another means to do this is to setup a jndi.properties file and use the no-arg InitialContext constructor. In this case the application server will access the jndi.properties file at runtime. How this is done is shown in the code below:
Java Code: InitialContext with arguments constructorProperties env = new Properties(); // Set container specific JNDI properties InitialContext ctx = new InitialContext(env);
Once this is completed, you can access remote EJB or other resources:Java Code: InitialContext without arguments constructorInitialContext ctx = new InitialContext();
Another means of doing this is by using the @Resource annotation. This annotation can be used for doing injection with a wide variety of resources (i.e. environment entries, EJB references, JDBC data source, email, JMS resources, etc). An example of how this is done is shown below:Java Code: Dependency Injection using Initial ContextTimeService timeService = (TimeService) ctx.lookup(“ejb30.session.TimeService”);
As this is a straightforward JNDI resource, it would be straightforward for the EJB container to inject this into the bean. This is similar to doing the same using a deployment descriptor:Java Code: Dependency Injection using @Resource annotation@Stateless public class FXMarketBean implements FXMarket { ... @Resource(name="jdbc/fxMarketDB") private javax.sql.DataSource dataSource;
In both cases, the name parameter of using the @Resource or the res-ref-name in the deployment descriptor, they are translated to a fully qualified JNDI mapping (i.e. java:comp/env/[name]). The container then resolves the JNDI references for the resources and then binds the resource to the environment naming context (i.e. java:comp/env for JNDI) during deployment. The container will throw a runtime exception if it is unable to find the resource. This will leave the bean as unusable.XML Code: Dependency Injection using deployment descriptor<resource-ref> <res-ref-name>jdbc/fxMarketDB</res-ref-name> <res-type>javax.sql.DataSource</res-type> </resource-ref>
Annotations with EJB3
With the inception of EJB3, it is now possible to do service configuration using Java metadata annotations. This was a significant change from EJB2 where one was required to use XML deployment descriptors. Annotations simplify the EJB programming model by removing this need for detailed deployment descriptors. It also is an effective delivery mechanism for dependency injection.
Basically, annotations allow you to “add” additional attributes to a Java class, interface, method, or variable. These attributes can be used in a development environment like Eclipse, with a persistence provider like Hibernate, in the runtime environment like the Java EE container, with the Java compiler, or a deployment tool, .Annotations are used like Java modifiers that can be used by anything handling Java source or byte code. An example of a is shown below:
The @Stateless and @LocalBean symbols are annotations. What this tells you is that the ViewMonteCarloBean Java class is a local bean and a stateless session bean. Note that we are able to do this by adding a bit of extra information about the class without needing us to implement an interface, extend a class, or add a member variable or method. As annotations are a special kind of interface, we need to import the relevant packages where they are defined. In this case, the @LocalBean and the @Stateless annotations are defined in the javax.ejb.LocalBean.class and javax.ejb.Stateless files respectively. This is for the benefit of the compiler. The runtime environment decides how to use the annotations; in this case to define the POJO as a local stateless session bean. Note that the annotation facility now makes attribute-oriented programming a core part of the Java language.Java Code: Annotation example for Stateless Local Beanimport javax.ejb.LocalBean; import javax.ejb.Stateless; @Stateless @LocalBean public class ViewMonteCarloBean extends MonteCarloBeanBase
Finally it isn’t a good idea to mix and match configuration with source code such as annotations. If you did, you would need to change source code each time you made a configuration change for anything from a database connection resource to a deployment environment entry. EJB 3 addresses this situation by allowing you to override annotations via XML deployment descriptors where appropriate. This is because in EJB 3 they’re designed to harmonious coexist. This is an invaluable feature for enterprise applications that can be deployed to a variety of environments such as for a test and a production server. The best way of mixing and matching annotations and XML metadata is to use annotations for everything excluding environment–specific configurations. This should be left for XML.
In the development of Java EE 5.0, it become clear that there was a need to develop a set of annotations for EJB3 in order to facilitate integration with the web tier. They are applicable across servlets, JSF managed beans as well as application clients. You can find them in the javax.annotation.* package. The list of some of the common metadata annotations is provided below.
Note that common metadata annotations such as @Resource, @EJB, @WebServiceRef, @PersistenceContext, and @PersistenceUnit annotations are primary focused on dependency injection avoiding many of the complexities of doing JNDI lookups.
XML Deployment Descriptors
As we mentioned above, XML deployment descriptor is simply an XML file that holds application configuration information. This deployment descriptor describes its contents and environment. In Java EE5, there are various deployment descriptors for the different deployment units. The examples are the Enterprise Archive (EAR), EJB (ejb-jar) and Web Application Archive (WAR) modules. One of the original problems of deployment descriptors was that they were verbose and difficult to make and understand. Alot of the items included were boilerplate items that could have been added automatically unless someone expressly provided by the developer or deployer.
Now with EJB3 and the advent of annotations, best practices have dictated that for application specific items that they be left to annotations and that we use deployment descriptors for environmental specific items. This doesn’t prevent the deployer or developer from overriding configuration of the application, as the deployment descriptor will override the annotation, but it provides for a good separation of concerns.
Now that we have covered key parts of the EJB Model that are common across all EJBs whether they are Session or Message Driven Beans or even Entities. Now let’s go through other features and services provided by EJBs where they are differences between the different type of EJBs.
The EJBContext
In order to gain access to key container services such as transaction or security, you will need to do this using the EJBContext. It is possible to use dependency injection in order to obtain a SessionContext. In the case of a session bean, there are a number of methods that are added specific to the session bean’s environment. These methods are:
getBusinessObject()
getEJBLocalObject() - For EJB2 Beans
getInvokedBusinessInterface()
getMessageContext
An example of a SessionContext being added is shown below:
For message driven beans, there is a similar object called a MessageDrivenContext. Unlike a SessionContext, there are no methods that have been added to it. Instead it will throw exceptions if getEJBHome, getEJBLocalHome or isCallerInRole are called. This is because since the MDB is running in a messaging environment, there is no point in adding methods on a business interface. The means of injecting a MessageDrivenContext into an MDB is shown below:Java Code: Obtaining a session context using dependency injection@Stateless public class PlaceBidBean implements PlaceBid { @Resource SessionContext context; ... }
Transaction Management with EJB3Java Code: Obtaining a message driven context using dependency injection@MessageDriven public class OrderBillingMDB { @Resource MessageDrivenContext context; ... }
EJBs use the Java Transaction API (JTA) for Transaction management support. The JTA is a high-level API in which the application server provider implementation normally exposes functionality at the distributed transaction manager layer. In fact most EJB developers in general need to know about only one JTA interface: javax.transaction.UserTransaction. The reason for this is that the EJB container handles most of the transaction management details. EJB developer only needs to tell the container where the transaction boundary begins and ends and whether to roll back or commit.
There are two ways of using transactions in EJB:
Container-managed transaction (CMT) - used to is to declaratively manage transactions
Bean-managed transaction (BMT) - requires you to explicitly manage transactions programmatically
Both provide abstractions over JTA. Using CMT you can declaratively manage transactions using either annotations or the deployment descriptor. It is important to note that in this version of EJB, only session beans and MDBs support BMT and CMT. The EJB 3 Java Persistence API is not directly dependent on either CMT or BMT but can transparently plug into any transactional environment while used inside a Java EE container. An example of a CMT transaction is shown below:
First the bean is annotated with the @TransactionManagement that tells the container we are using container managed transactions. We then have a EJB Context which is injected into the bean. For the placeFXOrder we have annotated the method with transaction required attribute which means that a transaction will be started by the container whenever this method is invoked. Note within the method that it is specified to rollback the transaction using the EJBContext’s object setRollbackOnly method.Java Code: Annotation a session bean and method for transactions@Stateless @TransactionManagement(TransactionManagementType.CONTAINER) public class OrderManagerBean { @Resource private SessionContext context; ... @TransactionAttribute(TransactionAttributeType.REQUIRED) public void placeFXOrder(FXItem fxItem, Customer customer){ try { if (!bidsExisting(fxItem)){ validateCredit(customer); chargeCustomer(customer, fxItem); removeItemFromBidding(fxItem); } } catch (CreditValidationException cve) { context.setRollbackOnly(); } catch (CreditProcessingException cpe){ context.setRollbackOnly(); } catch (DatabaseException de) { context.setRollbackOnly(); } } }
EJB3 Security
The two functions of security is to provide authentication (i.e. the process of verifying one’s identity) and authorization (i.e. the process of determining whether the user has access to a resource or task). Both functions are necessary to provide robust security for your application. Authorization is intimately linked with other concepts of security such as users, groups and roles. Java EE security is based primarily on the Java Authentication and Authorization Service (JAAS) API. It provides a pluggable API that facilitates decoupling between the Java EE application and the actual security service since the application interfaces with the service via JAAS API. The JAAS API then has implementations that allow it to talk to Microsoft Active Directory, Novell Directory, Oracle Internet Directory (OID) or Lightweight Directory Access Protocol (LDAP).
JAAS lets both authentication and authorization to be performed at any Java EE tier. Most application are accessible via the web and will share its authentication system across various tiers. Once an individual is properly authenticated, JAAS will provide the system with a valid user principal object which is associated with one or more roles. The javax.security.Principal interface has methods which allows the application to check whether the principal/role is authorized to access a particular secure resource. The Principal can be transparently moved from the web tier to business tier when required.
The configuration of web security is kept in the login-config and security-constraint elements of the web.xml file. Below is a configuration showing how to secure the administrative module.
This uses the simplest authentication mechanism, BASIC which uses HTTP header-based authentication scheme. This scheme causes the web browser to gather username/password information using a built-in prompt. The other section concerns the realm (i.e. a container specific abstraction over a JAAS driven authentication system) the container needs to authenticate against. Note that it is specified that all URLS that match the pattern /admin/* need to be secured and that only validated principals with the role ACCTMGR can access these resources. Unless you are interested in using programmatic security, this is the essential pattern that is used for declarative security.XML Code: Configuration of authentication and authorization for site<login-config> <auth-method>BASIC</auth-method> <realm-name>FxMarketRealm</realm-name> </login-config> ... <security-constraint> <web-resource-collection> <web-resource-name> FXMarket Administrative Component </web-resource-name> <url-pattern>/admin/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>ACCTMGR</role-name> </auth-constraint> </security-constraint>
Session Beans
As EJBs have been simplified in EJB3, all session beans are made up the two basic parts of any plain old java object (POJO), an interface and an implementation of the bean. There are many benefits to this such as being able to apply design principles such as use construction rather than inheritance. Other benefits are by having all clients access beans through their interface it drastically simplifies the unit-testing of the business logic provided by the session bean as well as facilitating the use of dependency injection. The interface for the POJO is know as the business interface in EJB lingo and the implementation is called the Bean or Bean implementation.
There are a number of rules that applies to EJB3 beans. I will outline them here:
- Session Beans must have at least one business interface
- Session Beans must be concrete (i.e. not defined as final or abstract)
- Session Beans must have a no-argument constructor
- Session Beans can subclass another session bean or POJO (benefit of the POJO based EJB model)
- Session Beans business methods and/or lifecycle callback methods may be defined in either the bean class or in a superclass. Note that you can also do annotation inheritance with limitations on the inheritance of annotations for lifecycle methods and resource injections. They all will be inherited by the bean class.
- Session Bean’s business method must not start with “ejb” and must be public. You cannot use final or static in the definition of a business method.
The Lifecycle of EJBs
Across all types of EJBs, there are two events that are consistent across their lifecycles. Those events are creation and destruction. For stateful session beans, there are two other phases that they go through called passivation which is when a stateful session bean is serialized to some form of storage and activation which is when the session bean is uploaded from storage and reactivated. The lifecycle of all beans starts with the instantiation of a session bean:- The client obtains a reference either via dependency injection or from a JNDI lookup.
- The container invokes the newInstance method on the bean object which translates to a constructor invocation on the bean implementation class.
- For beans using dependency injection, all dependencies are resolved meaning the the relevant resources, beans and environmental components are injected into the new bean instance.
- After a period of time of use, when the container determines that the instance of a bean is not needed, that instance is destroyed.
Stateless Session Bean (SLSB) Lifecycle
Lifecycles don’t get much simpler than the lifecycle for a stateless session bean. It either doesn’t exist or it is ready for use. Formally these states are known as Does Not Exist, which is the initial state of every SLSB and Method-Ready Pool in which it’s business method can be invoked. In the Method-Ready Pool state, there is a pool of instantiated stateless session beans that are not being used but ready for use. This is the key difference with Stateful Session Beans that don’t define instance pooling. Finally note that a session bean's lifecycle is controlled by the container.
These are the steps that are performed by container for stateless session beans:- The bean is instantiated
- The container injects the bean’s SessionContext if required.
- The container performs any other dependency injection needed by any of the bean’s metadata
- The container invokes a PostConstruct callback method if it is present in the stateless session bean. The PostConstruct method can then be used for initializing any resources used by the bean. The PostConstruct method is only called once during the lifecycle of the instance when it transitions from the does-not-exist state to the method-ready pool.
Outside of what was mentioned above about stateful session beans not defining an instance pool, there are other areas in which they differ from stateless session beans. Stateful session bean starts from a does-not-exist state and moves to a method-ready state once a client does a business interface lookup or a dependency injection of a bean. This is initiated by the client and not by the container. Similarly to stateless session beans, once a stateful session bean is in a method-ready state, the business method can be invoked.
These are the steps that are performed by container for stateful session beans:- The SSB is instantiated
- The EJB container injects the bean’s SessionContext if required
- The EJB container performs any other dependency injection needed by any other the bean’s metadata
- The EJB container invokes a PostConstruct callback method if it is present in the bean
Message Driven Bean Lifecycle
Message Driven Bean’s Lifecycle parallels the lifecycle of stateless session beans. As both don’t need to maintain any state information, they are instantiated when the container is started and then held in a method ready pool. The one difference is that the container may decide at any time to create new instances of MDBs.
These are the steps that are performed by container for message driven beans:- The Message Driven Bean is instantiated
- The EJB container injects the bean’s MessageDrivenContext if required
- The EJB container performs any other dependency injection needed by any other the bean’s metadata
- The container invokes a PostConstruct callback method if it is present in the message driven bean. The PostConstruct method can then be used for initializing any resources used by the bean. The PostConstruct method is only called once during the lifecycle of the instance when it transitions from the does-not-exist state to the method-ready pool. The method can have any name but must have no arguments and have void as a return.
Local Interfaces
As most invocations of Session Beans are done remotely outside of the EJB container via RMI-IIOP protocol, this has a significant impact on performance since method arguments must be passed by value (i.e. serialized then passed over the network and deserialized). This is often the case even when one session bean is invoking another session bean within the same container. it is for this reason that EJB platform provides the possibility of having a local interface for a session beans. In this case, for method invocations, it is possible to pass method arguments by reference which significantly improves performance. The way this is done is by adding the @Local annotation to the bean interface. So it would look like the following:
By annotating the interface with @Local, this tells the EJB container that this bean can only be invoked by a local client running in the same container. So the implementation might look something like this.Java Code: EJB Bean defined as having a local interfacepackage com.acme.ejbexamples.session; import javax.ejb.Local; import com.acme.ejbexamples.domain.Flight; @Local public interface MyFlightService { public Flight getFlight(); }
Of course this is not very useful but in order to provide a description of how to implement a local interface, it will suffice.Java Code: Invocation of a local annotated EJB by a local clientpackage com.acme.ejbexamples.session; import java.util.*; import com.acme.ejbexamples.domain.Flight; @Stateless public class MyFlightServiceBean implements MyFlightService { public Flight getFlight(){ return new Flight(“Air France 1099”); } }
Interceptors
Interceptors come out of the concepts behind Aspect Oriented Programming (AOP). The key idea around AOP is to separate common concerns from business methods. Common concerns that are applicable across all parts of an application can be logging, auditing, transactions, security and performance monitoring. What this technique allows is to encapsulate all methods or classes addressing these concerns in interceptors or aspects. Now in EJB3, it makes strategic use of interceptors whenever a business method is invoked by the EJB container.
This ability of the EJB to apply interceptors to business methods of session and message driven beans allows the developer to customize the behavior of your application. All interceptors use the @AroundInvoke annotation on the methods that it applies. The interceptor can be defined in the bean class or separately in its own class. There is only one interceptor method per class.
Below is an example of how an interceptor can be used to measure execution times for the invocation of a specific method:
As mentioned above, we have an @AroundInvoke annotation that identifies the methodStats method as an interceptor. Now we can use annotations to specify interceptors (or you can use an XML deployment descriptor). When you create an interceptor method, you can name it as you wish but it must have a return type of Object, throw Exception and have a javax.interceptor.InvocationContext object as a parameter. The InvocationContext interface has a number of methods which provide information regarding the business method being invoked.Java Code: Modification of session bean with an interceptorpackage com.acme.ejbexamples.session; import javax.ejb.Stateless; import javax.persistence.EntityManager; import com.acme.ejbexamples.entity.Currency; import javax.persistence.PersistenceContext; import javax.interceptor.AroundInvoke; import javax.interceptor.InvocationContext; @Stateless public class ForexServiceBean implements ForexService { ... @AroundInvoke public Object methodStats(InvocationContext invctx) throws Exception { String name = invctx.getMethod().getName(); long startTime = System.nanoTime(); System.out.println("Starting method: " + name); try { return invctx.proceed(); } finally { long elapsedTime = System.nanoTime() - startTime; System.out.println("Finished method: " + name + " Method took " + elapsedTime + " nanoseconds to execute"); } } }
Note that we are able to access the name of the method by using:
That’s because the InvocationContext.getMethod() method returns, the business method invoking the interceptor. You can then use The Method.getName() method to obtain the invoking method name:Java Code: Code for accessing name of method being interceptedString name = invctx.getMethod().getName();
The next thing to notice is the use of InvocationContext.proceed() method. The proceed() method will invokes the next interceptor if there is more than one interceptor associated with the current business method. It will chain through the series of interceptors if needed by then calling the next interceptor. Once it reaches the last in the chain or if there is only one interceptor it will invoke the proceed() method. Note that the signature of this method is the following:
Similar to the use in AOP of @Around advice wraps itself, around the method, theJava Code: Signature for proceed() methodpublic Object proceed() throws Exception
@AroundInvoke indicates that the intercept wraps itself around the business method. It will execute its own code prior to the proceed() method being executed before invoking the business method and then execute the code in the finally clause after the business method is invoked. You can only use one @AroundInvoke annotation method per bean.
We will look at interceptors more in depth in another article. Finally if you want to use an XML descriptor to apply an interceptor, it would have the following syntax:
The preceding XML is a complete deployment descriptor. The <interceptor-binding> element specifies that we want the ForexServiceBean inceptor is executed when the cashAndCarry method is called in the MonteCarloBean.XML Code: XML descriptor for applying an interceptor<ejb-jar xmlns="http://java.sun.com/xml/ns/javaee" 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" version="3.1"> <assembly-descriptor> <interceptor-binding> <ejb-name>MonteCarloBean</ejb-name> <interceptor-class>com.acme.ejbexamples.business.ForexServiceBean</interceptor-class> <method-name>cashAndCarry</method-name> <method-params> <method-param>int</method-param> </method-params> </interceptor-binding> </assembly-descriptor> </ejb-jar>
Finally there are a number of lifecycle callback methods that are available from an interceptor class. They work in the same manner as the lifecycle callback methods (i.e. @PostConstruct, @PreDestroy, @PrePassivate and @PostActivate annotations) that are described for session beans to receive lifecycle callbacks. They are found in the lifecycle callback interceptors or listeners. As the target bean moves through it’s lifecycle, the annotated methods in the interceptor class are triggered. An example is shown below:
Note that lifecycle interceptor methods cannot throw checked exceptions as there is no client for lifecycle callbacks for the problem to propagate up to. As well, a bean’s lifecycle callbacks in the bean can also have one or more interceptors. This is the point of calling InvocationContext.proceed method in lifecycle interceptor methods. It doesn’t matter whether the interceptor class is applied with or without lifecycle callbacks. The bean lifecycle method or invocation chain will be triggered nonetheless. As I mentioned before these callbacks are often used for acquiring and releasing resources used by the beans. In any case they are extremely useful for numerous reasons.Java Code: Injector that intercepts lifecycle callback methodspublic class AuctionsResourceLogger { @PostConstruct public void initialize (InvocationContext context) { System.out.println ("Allocating resources for bean: " + context.getTarget()); context.proceed(); } @PreDestroy public void cleanup (InvocationContext context) { System.out.println ("Releasing resources for bean: " + context.getTarget()); context.proceed(); } }
Interceptor Lifecycle
The lifecycles of interceptor classes is the same as the EJBs they intercept. Refer to Figures 2-4 to see the lifecycles for the different types of EJBs. The interceptors are created, destroyed, passivated and activated alongside the bean instances. You can consider them as an extension of the EJB’s bean instance. They also have the same restrictions as the beans to which they are attached. So you can only inject an extended persistence context into a stateful session bean. You could not inject an extended persistence context into a stateless session bean or message driven bean.
The interceptor can also hold internal state since they like the beans to which they are attached have lifecycles and can hook into lifecycle events. This can be used to obtain a resource such as a database, message systems or another remote system as well as close the connection to that resource. Another thing that can be done is to maintain the state that is specific to the bean instance of which the interceptor is attached. This can be useful if you need to do a special cleanup after the bean instance is destroyed.
Exception Handling with Interceptors
Interceptors have very simple exception handling which involves putting a try->catch->finally block around the InvocationContet.proceed() method. In this way, it is possible to abort any invocation before it reaches the target bean’s method by throwing an exception within the @AroundInvoke or callback method, catch it and either rethrow a new exception that can be handled as it propagates up to the client or suppressing the exception. Another possibility is to retry the bean’s method call after catching an exception from the previous call of the bean’s method.
Custom Injection Using JNDI
Sometimes it could be useful to intercept an EJB Callback for when you want to create and define a custom injection annotation. For example, there are some application servers that like to use JNDI as a global registry for configuration or for .NET or other services. In this case, you can define your own annotation for providing this functionality and use an interceptor in order to implement it. An example of how to do this is provided below.
You would provide the global JDNI names as the value of the value() attribute of the @com.acme.ejbexamples.annotation.JndiInjected annotation. The custom annotation is then used in the following manner:Java Code: Annotation for defining custom injection of JNDIpackage com.acme.ejbexamples.annotations; import java.lang.annotation.*; @Target({ElementType.METHOD, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface JndiInjected { String value(); }
In this case we are doing global transactions and need a reference to a Java EE JTA Transaction Manager service. In general these are referenced from the global JNDI of the application server provider. In this situation, we can use this custom annotation to pull a reference from from the global JNDI for a field in the session bean. This is shown below using an interceptor class:Java Code: Example of using custom injection of JNDI@Stateless public class CustomSessionBean implements CustomSession { @JndiInject(“java:/TransactionManager”) private javax.transaction.TransactionManager tm; ... }
So the javax.annotation.PostConstruct is now annotated to JndiInject() method and is able to inform the EJB container that the EJB callback is going to be intercepted by the JndiInjector interceptor. The method uses reflection to find the the method and fields that annotated with @JndiInjected, it looks up the referenced JNDI name, and initializes the field or method of the target bean instance. Note that this is done in a try catch sequence since you cannot throw a checked exception but must be wrapped into an EJBException. It is now ready to apply this interceptor across all EJBs.Java Code: Custom JNDI Injectorimport java.lang.reflect.*; import com.titan.annotations.JndiInjected; import javax.ejb.*; import javax.naming.*; import javax.interceptor.*; import javax.annotation.*; public class JndiInjector { @PostConstruct public void jndiInject(InvocationContext invocation) { Object target = invocation.getTarget( ); Field[] fields = target.getClass().getDeclaredFields( ); Method[] methods = target.getClass().getDeclaredMethods( ); // find all @JndiInjected fields/methods and set them try { InitialContext ctx = new InitialContext( ); for (Method method : methods) { JndiInjected inject = method.getAnnotation(JndiInjected.class); if (inject != null) { Object obj = ctx.lookup(inject.value( )); method.setAccessible(true); method.invoke(target, obj); } for (Field field : fields) { JndiInjected inject = field.getAnnotation(JndiInjected.class); if (inject != null) { Object obj = ctx.lookup(inject.value()); field.setAccessible(true); field.set(target, obj); } } invocation.proceed( ); } catch (Exception ex) { throw new EJBException (“Failed to execute @JndiInjected”, ex); } } }
EJB Timer Service
In order to meet the requirements of modern enterprises, it is necessary for EJB Containers to provide EJBs with scheduling capability. This is known as the EJB Timer Service and is one of the container managed services that must be provided by any JEE 5 certified container. The EJB timer service uses the concept of time-delayed callbacks. The timer is actually a javax.ejb.Timer object that notifies the container of a timed event to be scheduled. What this means is that the service allows you to specify a method (i.e the timeout method) which is invoked automatically after a specified interval of time. The scheduling capability is separated between the EJBs that initiates (via a callback) the scheduler and the EJB method which encapsulate the business process that must run at that specific time. As this implementation of the EJB Timer is standardized across all EBJ Containers, your use of this service will work across all EJB containers.
You can only use either stateless session beans or message driven beans with the EJB timer service. This is because the timer is intimately tied into the EJB container. Therefore EJB entities which are not container managed would not be able to use the EJB Timer Service. The way to use a timer is to have an EJB method register a time-driven callback with the EJB container timer service. On the expiration of the time interval, the timeout method will be invoked. This is shown below.
We first access the timer service via the EJB context, and then the EJB creates a timer by invoking the TimerService.createTimer() method. The same EJB has a @Timeout annotated callback method, updateMktValue() that is invoked by the container when the scheduled event occurs. In the example, this method containing business process logic would update the positions of the portfolio of FX positions.Java Code: Example of Timer Service usepublic class FXMarketBean implements FXMarket { .... private void AddNewMarket(String userId, String mkt) { try { .... TimerService timerService = sc.getTimerService(); // Create a single event timer that expires after twelve hours. timerService.createTimer(43200000, mkt); System.out.println("Mkt timer created."); } catch (Exception e) { e.printStackTrace(); } } ..... @Timeout public void updateMktValue(Timer timer) { System.out.println("Updating portfolio value of Market: " + timer.getInfo()); // Implement your business logic here to update market value for fx positions return; } }
The timer interface
A Timer is simply a Java EE representation of a scheduled task. If you want to register timer, it is necessary to the TimerService interface in order to accomplish it. The EJB timer service either made available to the EJB Bean via an injection using the @Resource annotation or through the EJB context. Below is a specification of the options available for using the TimerService interface:
Since the EJB container will pass back the Timer instance that triggered the timeout, you can use this to access information about the object that prompted this timeout or other useful information. The Timer interface provides a number of useful methods for doing this. Below is the list of methods and their descriptions.
EJB 3.0 / EJB 2.x interoperability
There will be times where you are required to access business functionality running on a legacy EJB 2 application. There are also times where rather than doing a complete upgrade to an existing EJB2 system, your manager decides that they want to modify the EJB2 system to access some new EJB3 based functionality. It is for that reason that the EJB3 specification requires that all compliant containers support EJB2. This will come as a relief to many business that are worried about upgrading their EJB based systems. Below we will show how interoperability between EJB2 and EJB3 functions.
Invoking EJB 2 from EJB 3
It is possible to invoke EJB 2 session or entity beans from EJB 3 session beans or MDBs. A very straightforward method of doing this is via dependency injection. An example of how to do this is shown in the example below where the EJB 2 bean (ChargeCredit) used for credit card transactions is accessed. You must simply use the @EJB annotation to inject an instance of a home object for ChargeCredit into an EJB 3 POJO:
You might remember in EJB 2, an EJB had both remote and home interfaces. In the case of the ChargeCreditEJB, we have ChargeCredit and ChargeCreditHome respectively. In this example we use the create method to get a reference to the remote interface, and then invoke the add business method on the bean. Here we have shown that EJB3 supports EJB 2 quite easily.Java Code: Example of invocation of EJB2 Bean within EJB3 Bean@Stateful public FXOrderBean implements PlaceOrder { .. @EJB public ChargeCreditHome creditHome; .. void chargeCreditCard(){ ... ChargeCredit chargeCredit = creditHome.create(); String confirmationNo = chargeCredit.add(billingInfo, amount); .. }
If you are interested in invoking an EJB 2 CMP entity bean from EJB 3, it too follows a similar straightforward approach as above. Assume that FXMarket used EJB 2 CMP entity beans for its persistence tier. In this case, we will use the PlaceBid EJB to persists the Bid bean:
We create the bean instance (BidDTO) by using a data transfer object (DTO). DTO is used to transfer business object state across the application tiers. Note that creating an entity bean instance will cause the container to persist the bean instance in the database.Java Code: Example of invoking EJB2 CMP entity bean within EJB3 Bean@Stateless public PlaceBidBean implements PlaceBid { ... @EJB public BidLocalHome bidLocalHome; ... BidLocal bidLocal = bidLocalHome.create(BidDTO); ... }
Invoking EJB3 from EJB2
If you want to use EJB3 session beans and entities from an EJB2 applications there are some hoops you need to jump through but it is possible. First, you need to remember that in order to access resources as well as your EJBs, you need to use classical JNDI lookups. Also you must declare in your ejb-jar.xml file, your EJB3 session bean. This is to establish a reference to the bean. It would look as follows:
You can use either a container-managed or an application-managed EntityManager in your EJB 2 beans. Assume that ActionBazaar migrated only the persistence tier to JPA and we reverse the context of the EJB Beans such that the PlaceBid EJB is a EJB2 session bean and the CheckCredit is a EJB 3 session bean. To use the container-managed EntityManager from an EJB 2 bean, you’d define the ejb-ref or ejb-local-ref for the EJB 2 bean as follows:
The difference between this deployment descriptor and a standard one is the lack of a local-home element in the ejb-local-ref since EJB3 session beans don’t require a home interface. You use JNDI for lookup as the beans using ref-name as shown below:XML Code: EJB2 Deployment Descriptor for EJB3 ejb-local-ref<session> <ejb-name>PlaceBidBean</ejb-name> <home>fxmarket.buslogic.PlaceBidHome</home> <remote>fxmarket.buslogic.PlaceBid</remote> <ejb-class>fxmarket.buslogic.PlaceBidBean</ejb-class> <session-type>stateless</session-type> ... <ejb-local-ref> <ejb-ref-name>ejb/CheckCredit</ejb-ref-name> <ejb-ref-type>Session</ejb-ref-type> <local>oldfxmarket.buslogic.CheckCredit</local> <ejb-link>oldfxmarket-ejb.jar#CheckCreditBean</ejb-link> </ejb-local-ref> </session>
To use an entity with your EJB2 beans, you use either the container managed or application managed EntityManager from the EJB2 bean in the following manner:Java Code: Lookup for EJB3 Bean in EJB2 Session Beanpublic class PlaceBidBean implements SessionBean { //EJB 2 bean ... public void addBid(Bidder user, double amount) { CheckCredit checkCredit = (CheckCredit) context.lookup("java:comp/env/ejb/CheckCredit"); checkCredit.addBid(user, amount); } @Stateless public class CheckCreditBean implements CheckCredit{ //EJB 3 bean }
Then, you look up an instance of a container-managed EntityManager viaXML Code: Deployment descriptor for definition of persistence-context-ref<session> <ejb-name>PlaceBidBean</ejb-name> ... <persistence-context-ref> <persistence-context-ref-name> FXMarketEntityManager </persistence-context-ref-name> <persistence-unit-name>fxMarket</persistence-unit-name> </persistence-context-ref> </session>
JNDI:
Note that the key thing to do is to package a persistence.xml that describes the persistence unit and set version="3.0" in the ejb-jar.xml. So outside of making modifications to the deployment descriptors and using JNDI to get object references it is not complicated to use EJB 3 beans or entities from EJB 2.Java Code: Lookup for EJB3 Entity ManagerContext context = new InitialContext(); EntityManager entityManager = (EntityManager) context.lookup("java:comp/env/ActionBazaarEntityManager"); ... entityManager.persist(bid);
OK. That’s it. In our next article we will continue our review of all things one is required to know for the component developer exam.
Similar Threads
-
Tutorial: EJB3 Overview for the Component Developer Exam
By Java Exam in forum OCPJBCDReplies: 0Last Post: 12-06-2011, 06:31 PM -
maximum view depth?
By Bit2_Gosu in forum New To JavaReplies: 0Last Post: 02-16-2009, 02:32 PM


LinkBack URL
About LinkBacks









Bookmarks