EJB3Labs

Version 4

    EJB3 Labs

     

    Set Up

     

    This section describes the setup steps required to run the three EJB3 labs covering Stateless Session Beans, Entity Beans and Interceptors.

     

    JVM

     

    1. Download and install a J2SE 5 JVM from http://java.sun.com

    2. Set up an environment variable called JAVA_HOME to point to the install directory

    3. Add JAVA_HOME\bin to your executable path

    4. Run java -version from a command line

    5. You should see java version "1.5.0_xx" ...

     

    JBoss AS

     

    1. Install JBoss AS 4.0.5 with the EJB3 profile using the JEMS installed from http://sourceforge.net/project/showfiles.php?group_id=22866&package_id=193295&release_id=477023

    2. Set up an environment variable called JBOSS_HOME to point to the install directory

     

    Apache Ant

     

    1. Download and install Apache Ant 1.6.5 from http://ant.apache.org

    2. Set up an environment variable called ANT_HOME to point to the install directory

    3. Add ANT_HOME\bin to your executable path

    4. Run ant -version from a command line

    5. You should see Apache Ant version 1.6.5 ...

     

    Lab Materials

     

    1. Download the labs materials from http://wiki.jboss.org/wiki/attach?page=EJB3Labs%2FJBossWorldBerlinLabs.zip

    2. Unzip into a new directory on your hard drive

     

    Lab One (lab-slsb-demo3) - Deploying a POJO as an EJB3 Stateless Session Bean

    This lab will show you how easy it is to take an existing POJO that simply implements your business methods and to deploy it as an EJB3 Stateless Session Bean using annotations.

     

    The POJO Base

     

    The example consists of two very simple classes. org.jboss.tutorial.stateless.bean.Calculator contains a standard Java interface which defines three business methods to perform mathematical operations on two ints. org.jboss.tutorial.stateless.bean.CalculatorBean provides an implementation of this interface that uses println statements to show what it is doing. Note that there is nothing special about these two classes - they are very simple Java.

     

    org.jboss.tutorial.stateless.client.Client is a simple client that can be used to excercise the business methods. It takes a single argument which determines how it should obtain an implementation of the Calculator interface (the -pojo option specifies that the client should create a new local CalculatorBean while the -ejb option specifies that the client should connect to a remote EJB implementation of CalculatorBean). The POJO option works with the code as it stands, this lab will walk you through implementing the EJB option.

     

    Open a command prompt in the src\build directory and run the command "ant runpojo". This compiles the code and runs the client with the POJO option. Here the client uses the constructor to directly create and access a CalculatorBean instance. You can see the interactions printed in the same command prompt by println statements from both the client and the implementation.

    Deploying as an EJB3

     

    It is very straightforward to create a Stateless session bean in EJB3. All bean types are homeless in EJB3. That means you only have to create a bean class, have it implement at least one interface, add some annotations to your bean class to indicate to the EJB3 container your intentions and you&146;ve created a Stateless bean.

     

    The javax.ejb.Stateless annotation marks a class as being a Stateless Session Bean. This annotation does not take any arguments and can be added to a class using the syntax @Stateless just before the class definition in the .java file.

     

    TASK: Import the Stateless annotation class and add the Stateless annotation to CalculatorBean.java.

     

    The javax.ejb.Remote annotation indicates the remote interface that corresponds to the EJB. It takes a single argument which is the class of the remote interface, specificied in brackets using the following pattern: @Remote(RemoteClass.class).

     

    TASK: Import the Remote annotation class and add the Remote annotation to CalculatorBean.java, specifying Calculator as the remote class

     

    Make sure JBoss is running and then open a command prompt in the src\build directory and run "ant ejbjar". This compiles the code and then JARs it into a file called lab-slsb-demo3.jar. If you investigate this file, you will note that it contains no XML config, only the two class files we have just annotated. This command also deploys the file by copying it to the JBoss deploy directory. JBoss scans the file automatically and discovers and deploys our new EJB. If you look at the output from the running JBoss instance you will see confirmation that it has discovered a new EJB in the JAR file and deployed it sucessfully.

     

    Finally, we need to tell the client where to find the EJB we have just deployed. One of the nice features of EJB3 is that it removes a lot of the need for pointless XML configurations by providing sensible defaults. For example, the remote interface for a class FooBar is bound by default to the location FooBar/remote.

     

    TASK: Open Client.java and edit line 40 to point to the correct location in the JNDI tree for the CalculatorBean stateless session bean.

     

    Open a command prompt in the src\build directory and run "ant runejb". This compiles the code and runs the client with the EJB option. Here the client uses a JNDI lookup to bind to the remote interface of the CalculatorBean EJB running in the JBoss AS and then accesses it via the Calculator interface. You can see the interactions printed in the different commands prompt by println statements in both the client (outputs to the ant prompt) and the implementation (outputs to the JBoss prompt).

     

    Conclusion

     

    In this lab we have seen how easy it is to use EJB3 annotations to deploy an existing POJO in a J2EE container and how EJB3's use of sensible defaults coupled with annotations remove much or all of the need for complex, unwieldy XML configuration files.

     

    Lab Two (lab-entity-demo4) - Deploying a POJO as an EJB3 Entity Bean

     

    This lab will show you how easy it is to take an existing POJO that simply represents one of your business objects and to deploy it as an EJB3 Entity Bean using annotations. Once this is done, your business objects will persist in a database and be searchable.

     

    The POJO Base

     

    The example is based on a very simple class. org.jboss.tutorial.entity.bean.Customer contains a standard Java class which defines a business object with four JavaBean style properties and accessors representing its state (a first name, a last name, a phone number and an ID for the Customer). Note that there is nothing special about this class - it is very simple Java.

     

    org.jboss.tutorial.entity.bean.CustomerManagerBean is an EJB3 Stateless Session Bean that implements the org.jboss.tutorial.entity.bean.CustomerManager interface and provides a set of operations to interact with instances of Customer. In many ways CustomerManagerBean is reminiscent of an EJB2 Home interface but not that it is implemented as a pure Java interface. We will not recap how to expose business methods in a Stateless Session Bean as that was covered in the first lab.

     

    The Entity Bean EJB

     

    It is very straightforward to create an Entity Bean in EJB3. You simply create a Java class that follows a simple set of conventions, add some annotations to your bean class to indicate to the EJB3 container your intentions and you&146;ve created a Entity bean.

     

    The javax.persistence.Entity annotation marks a class as being an Entity Bean (in actual fact a Java Persistance Bean which might run in or outside a JEE container but we will ignore this distinction for the moment). This annotation does not take any arguments and can be added to a class using the syntax @Entity just before the class definition in the .java file.

     

    TASK1: Import the Entity annotation class and add the Entity annotation to Customer.java.

     

    In order to function well with databases, persistent objects should have a unique identifier, or primary key. It is good practice to make this identifier a non-semantically loaded one (i.e. not to base it on the last name but rather on a meaningless ID). Customer already includes a field called id which is perfect for our purposes. The javax.persistence.Id is used to indicate the field in an Entity that correponds to the primary key. This annotation does not take any arguments and can be added to a class using the syntax @Id just before the getter method definition corresponding to the property holding the primary key value in the .java file.

     

    TASK2: Import the Id annotation class and add the Id annotation to Customer.java, specifying id as the property holding the primary key.

     

    This is all you need to do to enable your POJO to be persisted. Again, EJB3 remove much of the pain of uneccesary configuration by using "smart defaults". So, when a Customer Object is persisted to a remote database it will be stored in a table called CUSTOMER and each of the properties will be mapped to a column with the same name as the property and the appropriate data type.

     

    This is very convenient but sometimes you want more control over the mapping between the POJO and the database. In this case, you can use configuration by exception to configure just the things you wish to differ from the defaults.

     

    For example, the javax.persistence.Column annotation is used to override the default name of the column in the database to which a given field will be persisted. This annotation takes a single argument which is the name of the database column to which a property should be mapped using the following pattern: @Column(name="COLUMN_NAME").

     

    TASK3: Import the Column annotation class and add the Column annotation to Customer.java, specifying that the phone property should be stored in a column called "PHONE_NUMBER".

    EntityManager and the CustomerManagerBean

     

    EJB3 (again, more precicesly the Java Persistence API) introduces the javax.persistence.EntityManager to provide a generic mechanism with which to interact with persistent POJOs such as the Entity Bean we have just created. It is often more convenient to create a specific Java interface that is appropriate to the domain objects being used in a given project. The org.jboss.tutorial.entity.bean.CustomerManager is an example of such an interface which provides methods allowing for the creating, finding/searching, and deleting of Customer objects.

     

    In an EJB3 environment such interfaces are realised in concrete Stateless Session Bean implementations such as org.jboss.tutorial.entity.bean.CustomerManagerBean. If you investigate this class you will see that it declares a member variable of type EntityManager which is apparently never initialised but is made liberal use of in methods such as createCustomer

     

    One of the very nice new features of EJB3 is the concept of Inversion of Control (IoC) which allows the use of annotations to indicate the container what resources you would like it to automatically inject into your classes when they are required.

     

    Instead of having to initialise an EntityManager by hand, the javax.persistence.PersistenceContext annotation is used to indicate which datasource we want to use and to automatically configure and inject an appropriate instance of EntityManager. This annotation takes a single argument which is the name of the persistence context column we wish to inject an EntityManager for using following pattern: @PersistenceContext(name="context-name").

     

    TASK4: Import the PersistenceContext annotation class and add the PersistenceContext annotation to CustomerManagerBean.java, specifying that an EntityManager instance should be injected configured for the persistence context "lab-entity-demo4".

     

    EntityManager provides a number of methods for interacting with persistence sources. One convenient method is Object EntityManager.find(Class entityClass, Object primaryKey) which takes a Class and a unique ID and returns the corresponding Object

     

    TASK5: edit CustomerManagerBean to fix the findCustomer(int id) method so that instead of returning null it uses the EntityManager.find method to return the Object of class Customer.class with the corresponding int as a primary key.

     

    The EJB container needs to understand how it should connect the "lab-entity-demo4" persistence context to the underlying data source. This is done via the persistence.xml file which is bundled into the deployed jar file with our Entity and Session bean. We will hook this persistence context up to the DefaultDS deployed with JBoss AS which is a Hypersonic instance for demo purposes.

     

    TASK6: edit resources\META-INF\persistence.xml so that the "lab-entity-demo4" persistence context points to java:/DefaultDS

     

    Running the Example

     

    Make sure JBoss is running and then open a command prompt in the src\build directory and run "ant ejbjar". This compiles the code and then JARs it into a file called lab-entity-demo4.jar. If you investigate this file, you will note that it contains only the simple persistence.xml config and the three class files we have just annotated. This command also deploys the file by copying it to the JBoss deploy directory. JBoss scans the file automatically and discovers and deploys our new EJBs. If you look at the output from the running JBoss instance you will see confirmation that it has discovered a EJBs in the JAR file and deployed them sucessfully.

     

    Open a command prompt in the src\build directory and run "ant runCreate". This compiles and runs the code in the org.jboss.tutorial.entity.client.ClientCreate class which creates five new Customer objects on the client-side and then uses CustomerManagerBean to persist them to a database on the server. In a real-world environment these objects would now persist over restarts of the JBoss AS environment (the DefaultDS is cleaned out on restarts). It then uses the findAllCustomers method to iterate over the saved Customer objects.

     

    Open a command prompt in the src\build directory and run "ant runFind". This compiles and runs the code in the org.jboss.tutorial.entity.client.ClientFind class which searches for the Customer with id=3 and all Customer objects with surnames that start with "S" (Burr Sutter and Stan Silvert).

     

    Open a command prompt in the src\build directory and run "ant runDelete". This compiles and runs the code in the org.jboss.tutorial.entity.client.ClientFind class which deletes the Customer with id=2 (Stan Silvert). Now rerun "ant runFind" and only Burr Sutter will appear

     

    Conclusion

     

    In this lab we have seen how easy it is to use EJB3 annotations to make an existing POJO business object into an Enterprise class database persistented in a J2EE container and again how EJB3's use of sensible defaults coupled with annotations remove much or all of the need for complex, unwieldy XML configuration files.

     

    Lab Three (lab-interceptor-demo5) - Adding a Custom Interceptor to an EJB3 Bean

     

    This lab will show you how easy it is to implement a custom interceptor class that you can wrap around an existing EJB3 bean and add your own customised behaviour.

     

    The Stateless Session Bean Base

     

    We will use the CalculatorBean Stateless session bean that we produced in the first EJB3 lab and refactor it so that we remove the logging methods from the Session Bean and put them in a more generic LoggingInterceptor class.

    Implementing the Interceptor

     

    Writing the Interceptor Class

     

    org.jboss.tutorial.interceptor.LoggingInterceptor contains a POJO scaffold on which we will build our Inteceptor. Note that this class does not have to extend a specific superclass or implement a specific interface to be an interceptor. It does require a method which follows the template public Object METHOD_NAME(InvocationContext ctx) throws Exception but METHOD_NAME can be a name of your chosing.

     

    TASK1: edit LoggingInterceptor.java so that the logging method has the more user friendly name log but keeps the same signature.

     

    The javax.interceptor.AroundInvoke annotation marks a method as being an interceptor method that can be used to wrap method calls to other EJBs. This annotation does not take any arguments and can be added to a class using the syntax @AroundInvoke just before the method definition in the .java file.

     

    TASK2: Import the AroundInvoke annotation class and add the AroundInvoke annotation to LoggingInterceptor.java, specifying log as the interceptor method.

     

    The flow of control within this method is important. The section before the try-catch-finally block will be called before the method that is being intercepted. In this block we make a note of the system time. The section in the try block needs to pass on control to the actual method call we are intercepting (actually, one can have chains of interceptors and this control may be passed to the next interceptor in the chain).

     

    The InvocationContext Object passed into our method has a number of methods controlling the flow of control between interceptors in the stack and the intercepted bean. InvocationContext.proceed() passes control on to the next interceptor in the stack or onto the method call itself

     

    TASK3: use the InvocationContext Object to pass control on to the bean.

     

    The finally block is exectuted after the wrapped bean method has been called and before we return from this method. In the finally block, we make a note of the system time again. Note that we still have access to the startTime variable and hence can calculate the elapsed time it took to execute this method.

     

    The InvocationContext Object passed into our method has a number of methods which give information about the method call that we are intercepting and the object on which the method call has been made. You can see how to obtain the Class of the bean on which the call has been made and the Method which has been called. InvocationContext.getParameters() returns an Object{FOOTNOTE DEF  } array of the actual parameters with which the method was called.

     

    TASK4: use the InvocationContext Object to obtain a list of the parameters passed to the method that is being intercepted.

     

    Finally, we print out all the information we have gathered. Note that this interceptor class is very generic. There is nothing in it which is specific to a particular EJB bean and it could be reused across many components.

    Adding the Interceptor to the CalculatorBean Stateless Session Bean

     

    The javax.interceptor.Interceptors annotation is used to add a stack of one or more interceptors to a method in an EJB. This annotation takes one or more Class paramters and can be added to a class using the syntax @Interceptors just before the class definition in the .java file.

     

    TASK5: Import the Interceptors annotation class and add the Interceptors annotation to CalculatorBean.java, specifying org.jboss.tutorial.interceptor.LoggingInterceptor.class as a single interceptor to use for all methods in the class.

     

    We have now refactored this class to remove cross-cutting logging concerns from the bean definition itself into a generic interceptor. This has the benefits of producing cleaner code and allowing better control. For example, turning off logging can be done in a single place and if/when we moved from println statements to a better logging framework changes would only be required in LoggingInterceptor rather than across all of our beans.

     

    TASK6: Remove the System.out.println statements from CalculatorBean.java

     

    Running the Lab

     

    Make sure JBoss is running and then open a command prompt in the src\build directory and run "ant ejbjar". This compiles the code and then JARs it into a file called lab-slsb-demo3.jar (NOTE: this overides the deployment from lab one). If you investigate this file, you will note that it contains no XML config, only the two class files we have just annotated. This command also deploys the file by copying it to the JBoss deploy directory. JBoss scans the file automatically and discovers and deploys our new EJB. If you look at the output from the running JBoss instance you will see confirmation that it has discovered a new EJB in the JAR file and deployed it sucessfully.

     

    Open a command prompt in the src\build directory and run "ant runejb". This compiles the code and runs the client with the EJB option. Here the client uses a JNDI lookup to bind to the remote interface of the CalculatorBean EJB running in the JBoss AS and then accesses it via the Calculator interface. You can see the interactions printed in the different commands prompt by println statements in both the client (outputs to the ant prompt) and the intereceptor (outputs to the JBoss prompt).

     

    Conclusion

     

    In this lab we have seen how easy it is to use EJB3 annotations to implement reusable interceptors and wrap them around method calls in your EJB3 beans.