For this exercise we’ll make a simple SLSB, a Calculator Service, to illustrate how the various pieces all fit together. In fact, we’ll spin up two EJBs—one to show the path of least resistance, and another to expose all business, home, and component views. The full source and deployment instructions for this example are available in Appendix A.
This is a great starting point, regardless of whether you’re
building EJBs. The contract, implemented as interfaces in Java, defines
what our service will do, and leaves it up to the
implementation classes to decide how it’s done.
Remember that the same interface cannot be used for both @Local
and @Remote
, so we’ll make some common base that
may be extended.
public interface CalculatorCommonBusiness { /** * Adds all arguments * * @return The sum of all arguments */ int add(int... arguments); } public interface CalculatorRemoteBusiness extends CalculatorCommonBusiness{}
As you can see, we’ve created CalculatorRemoteBusiness
by extending the
contract of CalculatorCommonBusiness
.
This will come in handy later when we want to add more views exposing
the same method (so we don’t have to rewrite its definition). Our remote
business interface meets the requirement that our session bean will have
at least one view, so we may now write the bean implementation
class.
Again we’ll make a common base to contain the logic, and extend it to add our metadata that will define the SLSB.
public class CalculatorBeanBase implements CalculatorCommonBusiness { /** * {@link CalculatorCommonBusiness#add(int...)} */ @Override public int add(final int... arguments) { // Initialize int result = 0; // Add all arguments for (final int arg : arguments) { result += arg; } // Return return result; } }
This contains the required
implementation of CalculatorCommonBusiness
.
add(int...)
. The bean implementation class
therefore has very little work to do.
import javax.ejb.LocalBean; import javax.ejb.Stateless; @Stateless @LocalBean public class SimpleCalculatorBean extends CalculatorBeanBase { /* * Implementation supplied by common base class */ }
The function of our bean implementation class here is to bring
everything together and define the EJB metadata. Compilation will embed
two important bits into the resultant .class file. First,
we have an SLSB, as noted by the @Stateless
annotation. And second, we’re
exposing a no-interface view, new to EJB 3.1, courtesy of the @LocalBean
annotation. Alternatively, we could
have used an XML descriptor to hold this information, making an EJB out
of CalculatorBeanBase
. The EJB
Container, upon deployment, will pick up on this information and take
the necessary actions.
Before we use our new classes as an EJB, let’s leverage the pure POJO programming model to make some quick tests.
Unit testing, as contrasted with integration testing, aims to isolate
small bits of functionality and assert that they’re working as expected.
The addition implementation of our CalculatorEJB
is an excellent candidate for
this kind of approach. By treating our EJB Implementation class as a
POJO, we can keep test setup and execution time light and quick.
Testing in this book will be implemented using the popular open source framework JUnit (http://www.junit.org/):
import junit.framework.TestCase; import org.junit.Test; public class CalculatorUnitTestCase { /** * Ensures that the CalculatorEJB adds as expected */ @Test public void testAddition() { // Initialize final CalculatorCommonBusiness calc = new SimpleCalculatorBean(); final int expectedSum = 2+3+5; // Add final int actualSum = calc.add(2, 3, 5); // Test TestCase.assertEquals("Addition did not return the expected result", expectedSum, actualSum); } }
Here we make a new instance of SimpleCalculatorBean
and use it to add a few
integers. Again, this is not an EJB. The example
showcases how the EJB Programming Model does not tie you to EJB at all;
the test has no knowledge of the specification APIs.
Integration testing is where we’ll ensure that the individual pieces play nicely together.
There are three steps involved in performing integration testing upon an EJB. First, we must package the sources and any descriptors into a standard Java Archive (JAR; http://java.sun.com/javase/6/docs/technotes/guides/jar/jar.html) or Enterprise Archive (EAR; http://java.sun.com/javaee/5/docs/tutorial/doc/bnaby.html#indexterm-47). Next, the resultant deployable must be placed into the container according to a vendor-specific mechanism. Finally, we need a standalone client to obtain the proxy references from the Container and invoke upon them.
The Sun Microsystems JDK, for example, ships with a standard
jar
tool that can be used to assemble classes,
resources, and other metadata into a unified JAR file, which will both
compress and encapsulate its contents. Typically your project’s build
will handle this step, often by way of an Apache Ant (http://ant.apache.org/) task or Maven (http://maven.apache.org/)
goal. Whichever route you choose in packaging, the result might look a
little bit like this if we were to print out the contents:
shell$> $JAVA_HOME/bin/jar -tvf firstejb.jar
0 Wed Mar 25 01:59:22 GMT-08:00 2009 META-INF/
129 Wed Mar 25 01:59:20 GMT-08:00 2009 META-INF/ejb-jar.xml // < OPTIONAL
0 Wed Mar 25 01:59:20 GMT-08:00 2009 com/
0 Wed Mar 25 01:59:20 GMT-08:00 2009 com/mycompany/
0 Wed Mar 25 01:59:20 GMT-08:00 2009 com/mycompany/
0 Wed Mar 25 01:59:20 GMT-08:00 2009 com/mycompany/
0 Wed Mar 25 01:59:20 GMT-08:00 2009 com/mycompany/
0 Wed Mar 25 01:59:20 GMT-08:00 2009 com/mycompany/firstejb/
318 Wed Mar 25 01:59:20 GMT-08:00 2009 com/mycompany/firstejb/CalculatorLocal
Home.class
248 Wed Mar 25 01:59:20 GMT-08:00 2009 com/mycompany/firstejb/CalculatorRemote
.class
348 Wed Mar 25 01:59:20 GMT-08:00 2009 com/mycompany/firstejb/CalculatorRemote
Home.class
1473 Wed Mar 25 01:59:20 GMT-08:00 2009 com/mycompany/firstejb/CalculatorBean
Base.class
237 Wed Mar 25 01:59:20 GMT-08:00 2009 com/mycompany/firstejb/CalculatorRemote
Business.class
939 Wed Mar 25 01:59:20 GMT-08:00 2009 com/mycompany/firstejb/ManyView
CalculatorBean.class
251 Wed Mar 25 01:59:20 GMT-08:00 2009 com/mycompany/firstejb/CalculatorLocal
.class
235 Wed Mar 25 01:59:20 GMT-08:00 2009 com/mycompany/firstejb/CalculatorLocal
Business.class
189 Wed Mar 25 01:59:20 GMT-08:00 2009 com/mycompany/firstejb/CalculatorCommon
Business.class
643 Wed Mar 25 01:59:20 GMT-08:00 2009 com/mycompany/firstejb/SimpleCalculator
Bean.class
The ejb-jar.xml
line
represents our optional XML descriptor, which is excluded from this
example.
The EJB Specification intentionally leaves the issue of deployment up to the vendor’s discretion. Therefore, our examples will keep this out of scope until Part V, which will fully detail instructions for running examples.
Again we’ll implement our client as a JUnit test. This time,
instead of creating POJOs via the new
operator, we’ll look up true EJB
references via JNDI. For our purposes, JNDI is a simple store from
which we may request objects keyed to some known address. The form of
the address is supplied by the EJB 3.1 Portable Global JNDI Name
Syntax, discussed in Chapter 16.
Let’s walk through this step-by-step. The process looks a bit like the following:
Perform initialization of the test, and set up the JNDI Environment.
Look up the proxy reference to the EJB via JNDI.
Invoke upon the reference.
Test the result.
First, we declare some imports to show where some nonobvious
types are coming from. Assume that the client is in the same package
namespace as the CalculatorEJB
classes:
import javax.naming.Context; import javax.naming.InitialContext; import org.junit.BeforeClass; import org.junit.Test;
The javax.naming
classes are used in obtaining the
proxy references from JNDI, and the others are part of the test
framework. Now to define the class and make for some static
members used in testing:
public class CalculatorIntegrationTestCase
{
/**
* The JNDI Naming Context
*/
private static Context namingContext;
/**
* The EJB 3.1 no-interface view view of the CalculatorEJB
*/
private static SimpleCalculatorBean calc;
/**
* JNDI Name of the no-interface view
*/
private static final String JNDI_NAME_CALC =
"java:global/myJarName/SimpleCalculatorBean";
Here we’ve declared a JNDI naming Context
for the lookups, and the names of
the target JNDI Addresses under which we’ll find the appropriate proxy
reference. Our tests will check that the EJB 3.1 Business View works
as expected, but before the test runs, a lifecycle method will obtain
the reference and set the proxy reference accordingly:
@BeforeClass // Invoked by the test framework before any tests run
public static void obtainProxyReferences() throws Throwable
{
// Create the naming context, using jndi.properties on the CP
namingContext = new InitialContext();
// Obtain EJB 3.1 Business Reference
calc = (SimpleCalculatorBean)
namingContext.lookup(JNDI_NAME_CALC);
}
First we create the naming Context
, using a standard jndi.properties
file that we assume to be present on the classpath. The JNDI
properties to be used are vendor-specific, so by pulling these out
into a separate file, our test remains portable across EJB implementations.
From there, we obtain the business proxy by performing a simple JNDI lookup upon the portable Global JNDI name.
Let’s define a central point to perform assertions within our tests:
private void assertAdditionSucceeds(CalculatorCommonBusiness calc) { // Initialize final int[] arguments = new int[] {2, 3, 5}; final int expectedSum = 10; // Add final int actualSum = calc.add(arguments); // Real EJB Invocation! // Test TestCase.assertEquals("Addition did not return the expected result", expectedSum, actualSum); }
This time, the calc
reference
passed in will not be a POJO, but instead a true EJB Proxy.
@Test public void testAdditionUsingBusinessReference() throws Throwable { this.assertAdditionSucceeds(calc); } }
Our two tests will merely pass the appropriate EJB Proxy to the assertion function. Assuming everything goes well, we’ll get the all clear and may rest assured that the system is working as we expect.
The interesting thing to note about this setup is that nowhere have we declared the notion of remoting, transactions, or the like. Regardless, this test is a fully functional remote client to a distributed architecture. Client code has no idea that it’s crossing process and network boundaries. This is a powerful argument in favor of the EJB Architecture.
Get Enterprise JavaBeans 3.1, 6th Edition now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.