Enterprise Java Development@TOPIC@
EJBs are based on Plain Old Java Objects (POJOs)
Standard POJO rules apply...
...
public class GreeterEJB implements Greeter {
private static final Logger logger = LoggerFactory.getLogger(GreeterEJB.class);
...
}
Must not write to static fields. Read-only static fields are allowed (suggests use of final)
Must not use thread synchronization primitives to synchronize execution
Must not manage/manipulate threads, class loaders, or security managers
Must not attempt to become a network server and form a network listen and similar actions
...
@Override
public String sayHello(String name) throws BadRequestException {
logger.debug("sayHello({})", name);
if (name == null || name.isEmpty()) {
throw new BadRequestException("you must have a name for me to say hello");
}
return "hello " + name;
}
Normal stateless POJO method at this point
import info.ejava.examples.ejb.basic.dto.Greeting;
import info.ejava.examples.ejb.basic.dto.Name;
public interface Greeter {
String sayHello(String name) throws BadRequestException;
Greeting sayHello(Name name) throws BadRequestException;
}
Provides an abstraction of the public class business methods
Some methods use simple/primitive types. Others use complex/custom classes.
All types conveniently defined using Serializable types at this point to permit use of remote interface in future
public class Name implements Serializable {
private String firstName;
private String lastName;
...
public class Greeting implements Serializable {
private Date date;
private String message;
...
Data abstraction necessary for local and remote interaction
Serializable not necessary until we use this in a remote interface
public class BadRequestException extends Exception {
public BadRequestException(String message) {
super(message);
}
}
Checked exceptions used to report business rule failures
Unchecked exceptions used to report infrastructure issues
When designing exceptions or response codes for POJO business methods -- it would be good to include the concept of:
Not Found -- one or more of the targets of the action could not be found
Bad Request -- nothing will improve if same request issued again
Internal Error -- nothing wrong with request and may be succesful at a later date
Also attempt to include specifics about the failed request to include IDs and parameters in question. This information is very valuable in determining whether the client or server is at fault and to more quickly determine the specific error and how it can be resolved.
import static org.junit.Assert.*;
import info.ejava.examples.ejb.basic.dto.Greeting;
import info.ejava.examples.ejb.basic.dto.Name;
import info.ejava.examples.ejb.basic.ejb.BadRequestException;
import info.ejava.examples.ejb.basic.ejb.Greeter;
import info.ejava.examples.ejb.basic.ejb.GreeterEJB;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class GreeterTest {
private static final Logger logger = LoggerFactory.getLogger(GreeterTest.class);
private Greeter greeter;
@Before
public void setUp() {
greeter = new GreeterEJB();
}
...
}
Test case used to verify the core functionality of the POJO -- prior to deployment as EJB
Each test case should be independent of another and not depend on a particular order
@Test
public void pojoGreeter() throws BadRequestException {
logger.info("*** pojoGreeter ***");
String name = "cat inhat";
String greeting = greeter.sayHello(name);
assertTrue("greeter did not say my name", greeting.contains(name));
}
@Test(expected = BadRequestException.class)
public void badName() throws BadRequestException {
logger.info("*** badName ***");
greeter.sayHello("");
}
@Test
public void dto() throws BadRequestException {
logger.info("*** dto ***");
Name name = new Name("thing", "one");
Greeting greeting = greeter.sayHello(name);
assertTrue("greeter did not say my name", greeting.getGreeting()
.contains(name.getFirstName()));
}
Each test method focused on testing a specific scenario or state
Contains assert statements to verify expected results
Each test method should be independent of another and not depend on a particular order
Production classes typically deployed in a Java archive (JAR)
$ jar tf target/ejb-basic-ejb-4.0.0-SNAPSHOT.jar ... info/ejava/examples/ejb/basic/ejb/BadRequestException.class info/ejava/examples/ejb/basic/ejb/Greeter.class info/ejava/examples/ejb/basic/ejb/GreeterEJB.class info/ejava/examples/ejb/basic/dto/Name.class info/ejava/examples/ejb/basic/dto/Greeting.class ...
|-- pom.xml `-- src |-- main | `-- java | `-- info | `-- ejava | `-- examples | `-- ejb | `-- basic | |-- dto | | |-- Greeting.java | | `-- Name.java | `-- ejb | |-- BadRequestException.java | |-- GreeterEJB.java | `-- Greeter.java `-- test |-- java | `-- info | `-- ejava | `-- examples | `-- ejb | `-- basic | `-- pojo | `-- GreeterTest.java `-- resources `-- log4j.xml
Module contains separate source trees for production and unit test code
Unit tests should run with every module build
Unit tests should auto-evaluate themselves so that no human analysis is required
$ mvn clean test ... [INFO] Deleting .../ejb-basic-ejb/target [INFO] [INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ ejb-basic-ejb --- [INFO] Using 'UTF-8' encoding to copy filtered resources. [INFO] skip non existing resourceDirectory .../ejb-basic-ejb/src/main/resources [INFO] [INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ ejb-basic-ejb --- [INFO] Changes detected - recompiling the module! [INFO] Compiling 5 source files to .../ejb-basic-ejb/target/classes [INFO] [INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ ejb-basic-ejb --- [INFO] Using 'UTF-8' encoding to copy filtered resources. [INFO] Copying 1 resource [INFO] [INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ ejb-basic-ejb --- [INFO] Changes detected - recompiling the module! [INFO] Compiling 1 source file to .../ejb-basic-ejb/target/test-classes [INFO] [INFO] --- maven-surefire-plugin:2.17:test (default-test) @ ejb-basic-ejb --- [INFO] Surefire report directory: .../ejb-basic-ejb/target/surefire-reports ------------------------------------------------------- T E S T S ------------------------------------------------------- Running info.ejava.examples.ejb.basic.pojo.GreeterTest 16:57:12,237 INFO (GreeterTest.java:41) -*** dto *** 16:57:12,243 DEBUG (GreeterEJB.java:41) -sayHello(Name [firstName=thing, lastName=one]) 16:57:12,249 INFO (GreeterTest.java:27) -*** pojoGreeter *** 16:57:12,250 DEBUG (GreeterEJB.java:27) -sayHello(cat inhat) 16:57:12,252 INFO (GreeterTest.java:35) -*** badName *** 16:57:12,255 DEBUG (GreeterEJB.java:27) -sayHello() Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.239 sec - in info.ejava.examples.ejb.basic.pojo.GreeterTest Results : Tests run: 3, Failures: 0, Errors: 0, Skipped: 0 [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 3.815 s ...
Basic POJO/JAR pom thus far
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>info.ejava.examples.ejb.basicejb</groupId>
<artifactId>ejb-basic-ejb</artifactId>
<packaging>jar</packaging>
<dependencies>
<!-- core implementation dependencies -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<scope>provided</scope>
</dependency>
<!-- test dependencies -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>