| Title: Simple Stateless Example |
| |
| <a name="SimpleStatelessExample-Overview"></a> |
| # Overview |
| |
| <object width="400" height="250"><param name="movie" |
| value="http://www.youtube.com/v/aLx2jta96xU?fs=1&hl=en_US"></param><param |
| name="allowFullScreen" value="true"></param><param name="allowscriptaccess" |
| value="always"></param><embed |
| src="http://www.youtube.com/v/aLx2jta96xU?fs=1&hl=en_US" |
| type="application/x-shockwave-flash" allowscriptaccess="always" |
| allowfullscreen="true" width="400" height="250"></embed></object> |
| |
| |
| This example shows how to create a Stateless session EJB using annotations. |
| As stated in the "JSR 220: Enterprise JavaBeansTM,Version 3.0 - EJB Core |
| Contracts and Requirements", |
| |
| |
| "Stateless session beans are session beans whose instances have no |
| conversational state. This means that all bean instances are equivalent |
| when they are not involved in servicing a client-invoked method. The term |
| 'stateless' signifies that an instance has no state for a specific client." |
| |
| |
| What this means is quite simply that stateless beans are *shared*. They do |
| in fact have state as you can assign values to the variables, etc. in the |
| bean instance. The only catch is there are a *pool* of identical instances |
| and you are not guaranteed to get the exact same instance on every call. |
| For each call, you get whatever instance happens to be available. This is |
| identical to checking out a book from the library or renting a movie from |
| the video store. You are essentially checking out or renting a new bean |
| instance on each method call. |
| |
| With EJB 3.0, it's now possible to write stateless session bean without |
| specifying a deployment descriptor; you basically have to write just a |
| remote or local business interface, which is a plain-old-java-interface, |
| annotated with the @Remote or @Local annotation the stateless session bean |
| implementation, a plain-old-java-object which implements the remote or the |
| local business interface and is annotated with the @Stateless annotation |
| |
| |
| |
| <a name="SimpleStatelessExample-TheCode"></a> |
| # The Code |
| |
| In this example we develop a simple EJB 3 Stateless session EJB. Every |
| stateless session bean implementation must be annotated using the |
| annotation *@Stateless* or marked that way in the ejb-jar.xml file. |
| |
| Our Stateless bean has 2 business interfaces: CalculatorRemote, a remote |
| business interface, and CalculatorLocal, a local business interface. A |
| minimum of one business interface is required. |
| |
| <a name="SimpleStatelessExample-Bean"></a> |
| ## Bean |
| |
| In EJB 3.0 session beans do not need to implement the javax.ejb.SessionBean |
| interface. You can simply annotate it as @Stateless if you want it to be a |
| stateless session bean. |
| |
| Users of EJB 2.x may notice the bean actually implements the business |
| interfaces! In the prior version of EJB implementing the remote interface |
| (which derives from javax.ejb.EJBObject) in your bean was just not allowed. |
| Now there is no javax.ejb.EJBObject requirement, so implementing the |
| business interfaces is standard practice for EJB 3.0. |
| |
| <a name="SimpleStatelessExample-Localbusinessinterface"></a> |
| ## Local business interface |
| |
| Local interfaces in EJB are *pass-by-reference* interfaces. Meaning that |
| *normal java semantics* are used for passing arguments, return values and |
| exceptions. A business local interface can be any plain java interface. |
| There are no restrictions on the method arguments, return types, or throws |
| clauses. |
| |
| Unless specified otherwise, every interface your bean implements (and it's |
| parent class implements and so on) is considered to be a local business |
| interface. You can use the *@Local* annotation to explicitly state that an |
| interface is a local interface, but this is not required. |
| |
| You'll notice that in EJB 3.0 the Local Business Interface of a stateless |
| session bean does not need to extend from javax.ejb.EJBLocalObject and does |
| not need a javax.ejb.EJBLocalHome interface as they did in EJB 2.x and |
| prior. Per the vocabulary of the EJB spec, interfaces that implement |
| javax.ejb.EJBLocalObject or javax.ejb.EJBLocalHome are considered Component |
| Interfaces and the plain java interface above is considered a Business |
| Interface. |
| |
| <a name="SimpleStatelessExample-Remotebusinessinterface"></a> |
| ## Remote business interface |
| |
| Remote interfaces are *pass-by-value* interfaces. Meaning that all method |
| parameters, return values, and exceptions are *serialized* on every call. |
| The result is that you get a copy of the original object and not the |
| original object. The advantage is of course that Remote interfaces can be |
| used to invoke an EJB across a network in a client-server fashion. There |
| are no restrictions on the Remote interface itself, but there are on the |
| data passed in and out of the remote interface. The *values* passed into a |
| method or returned from a method of a Remote interface *must be |
| serializable*. It is fine for the method signature to be, for example, |
| "public Object myMethod(Object myParam)" as long as the *value* passed in |
| and returned implements *java.io.Serializable*. |
| |
| As stated above, the Remote Business Interface of a bean can be any plain |
| old interface. It does not need to extend javax.ejb.EJBObject, it does not |
| need a javax.ejb.EJBHome, the methods do not need to throw |
| javax.rmi.RemoteException, and the bean class *can* implement it! |
| |
| At minimum the interface must be annotated with *@Remote* either in the |
| interface itself or in the bean class, or the interface must be declared |
| via <business-remote> in the ejb-jar.xml. |
| |
| <a name="SimpleStatelessExample-Writingaunittestfortheexample"></a> |
| # Writing a unit test for the example |
| |
| Writing an unit test for the stateless session EJb is quite simple. We need |
| just to write a setup method to create and initialize the InitialContext, |
| and then write our test methods |
| |
| <a name="SimpleStatelessExample-setUp"></a> |
| ## setUp |
| |
| <a name="SimpleStatelessExample-Testthelocalbusinessinterface"></a> |
| ## Test the local business interface |
| |
| <a name="SimpleStatelessExample-Testtheremotebusinessinterface"></a> |
| ## Test the remote business interface |
| |
| Note that JNDI names for Java SE clients are not standardized by the EJB |
| spec. This is unfortunate and something being addressed in EJB 3.1. The |
| default schema that OpenEJB uses is ejbName + interfaceType (i.e. Local, |
| Remote, LocalHome, RemoteHome), so in our example "CalculatorImpl" + |
| "Local" and "CalculatorImpl" + "Remote". You can in fact change this |
| default to be absolutely [anything you want](jndi-names.html) |
| including interface class name, ejb class name, and more. |
| |
| ## CalculatorBean |
| |
| package org.superbiz.stateless.basic; |
| |
| import javax.ejb.Stateless; |
| |
| @Stateless |
| public class CalculatorBean { |
| |
| public int add(int a, int b) { |
| return a + b; |
| } |
| |
| public int subtract(int a, int b) { |
| return a - b; |
| } |
| |
| public int multiply(int a, int b) { |
| return a * b; |
| } |
| |
| public int divide(int a, int b) { |
| return a / b; |
| } |
| |
| public int remainder(int a, int b) { |
| return a % b; |
| } |
| } |
| |
| ## CalculatorTest |
| |
| Our `CalculatorBean` can be easily tested using the `EJBContainer` API in EJB 3.1 |
| |
| package org.superbiz.stateless.basic; |
| |
| import junit.framework.TestCase; |
| |
| import javax.ejb.embeddable.EJBContainer; |
| |
| public class CalculatorTest extends TestCase { |
| |
| private CalculatorBean calculator; |
| |
| /** |
| * Bootstrap the Embedded EJB Container |
| * |
| * @throws Exception |
| */ |
| protected void setUp() throws Exception { |
| |
| EJBContainer ejbContainer = EJBContainer.createEJBContainer(); |
| |
| Object object = ejbContainer.getContext().lookup("java:global/simple-stateless/CalculatorBean"); |
| |
| assertTrue(object instanceof CalculatorBean); |
| |
| calculator = (CalculatorBean) object; |
| } |
| |
| /** |
| * Test Add method |
| */ |
| public void testAdd() { |
| |
| assertEquals(10, calculator.add(4, 6)); |
| } |
| |
| /** |
| * Test Subtract method |
| */ |
| public void testSubtract() { |
| |
| assertEquals(-2, calculator.subtract(4, 6)); |
| } |
| |
| /** |
| * Test Multiply method |
| */ |
| public void testMultiply() { |
| |
| assertEquals(24, calculator.multiply(4, 6)); |
| } |
| |
| /** |
| * Test Divide method |
| */ |
| public void testDivide() { |
| |
| assertEquals(2, calculator.divide(12, 6)); |
| } |
| |
| /** |
| * Test Remainder method |
| */ |
| public void testRemainder() { |
| |
| assertEquals(4, calculator.remainder(46, 6)); |
| } |
| } |
| |
| # Running |
| |
| Running the example should generate output similar to the following |
| |
| ------------------------------------------------------- |
| T E S T S |
| ------------------------------------------------------- |
| Running org.superbiz.stateless.basic.CalculatorTest |
| Apache OpenEJB 4.0.0-beta-1 build: 20111002-04:06 |
| http://tomee.apache.org/ |
| INFO - openejb.home = /Users/dblevins/examples/simple-stateless |
| INFO - openejb.base = /Users/dblevins/examples/simple-stateless |
| INFO - Using 'javax.ejb.embeddable.EJBContainer=true' |
| INFO - Configuring Service(id=Default Security Service, type=SecurityService, provider-id=Default Security Service) |
| INFO - Configuring Service(id=Default Transaction Manager, type=TransactionManager, provider-id=Default Transaction Manager) |
| INFO - Found EjbModule in classpath: /Users/dblevins/examples/simple-stateless/target/classes |
| INFO - Beginning load: /Users/dblevins/examples/simple-stateless/target/classes |
| INFO - Configuring enterprise application: /Users/dblevins/examples/simple-stateless |
| INFO - Configuring Service(id=Default Stateless Container, type=Container, provider-id=Default Stateless Container) |
| INFO - Auto-creating a container for bean CalculatorBean: Container(type=STATELESS, id=Default Stateless Container) |
| INFO - Configuring Service(id=Default Managed Container, type=Container, provider-id=Default Managed Container) |
| INFO - Auto-creating a container for bean org.superbiz.stateless.basic.CalculatorTest: Container(type=MANAGED, id=Default Managed Container) |
| INFO - Enterprise application "/Users/dblevins/examples/simple-stateless" loaded. |
| INFO - Assembling app: /Users/dblevins/examples/simple-stateless |
| INFO - Jndi(name="java:global/simple-stateless/CalculatorBean!org.superbiz.stateless.basic.CalculatorBean") |
| INFO - Jndi(name="java:global/simple-stateless/CalculatorBean") |
| INFO - Jndi(name="java:global/EjbModule181871104/org.superbiz.stateless.basic.CalculatorTest!org.superbiz.stateless.basic.CalculatorTest") |
| INFO - Jndi(name="java:global/EjbModule181871104/org.superbiz.stateless.basic.CalculatorTest") |
| INFO - Created Ejb(deployment-id=CalculatorBean, ejb-name=CalculatorBean, container=Default Stateless Container) |
| INFO - Created Ejb(deployment-id=org.superbiz.stateless.basic.CalculatorTest, ejb-name=org.superbiz.stateless.basic.CalculatorTest, container=Default Managed Container) |
| INFO - Started Ejb(deployment-id=CalculatorBean, ejb-name=CalculatorBean, container=Default Stateless Container) |
| INFO - Started Ejb(deployment-id=org.superbiz.stateless.basic.CalculatorTest, ejb-name=org.superbiz.stateless.basic.CalculatorTest, container=Default Managed Container) |
| INFO - Deployed Application(path=/Users/dblevins/examples/simple-stateless) |
| INFO - EJBContainer already initialized. Call ejbContainer.close() to allow reinitialization |
| INFO - EJBContainer already initialized. Call ejbContainer.close() to allow reinitialization |
| INFO - EJBContainer already initialized. Call ejbContainer.close() to allow reinitialization |
| INFO - EJBContainer already initialized. Call ejbContainer.close() to allow reinitialization |
| Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.068 sec |
| |
| Results : |
| |
| Tests run: 5, Failures: 0, Errors: 0, Skipped: 0 |