package com.tddinaction.ejb3.sessionbeans.calculator;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.PostActivate;
import javax.ejb.PrePassivate;
import javax.ejb.SessionBean;
import javax.ejb.Stateful;
import javax.ejb.Stateless;
import org.junit.Test;
/**
* An abstract base class for testing a session bean. The class contributes test
* methods for verifying the interface contract between the bean and the EJB
* container. For example, it checks that the proper class level annotations are
* in place, that the lifecycle annotations are tacked on the matching ejbXXX
* methods if the bean implements the EJB 2.X <tt>SessionBean</tt> interface,
* and that each lifecycle annotation occurs only once.
*
* @author Lasse Koskela
*/
public abstract class EJB3SessionBeanTestCase {
protected abstract Class<? extends Object> getBeanClass();
@Test
public void ejb3CompliantClassAnnotations() {
Annotation stateless = getBeanClass().getAnnotation(
Stateless.class);
Annotation stateful = getBeanClass().getAnnotation(
Stateful.class);
assertFalse("Only one of @Stateless or @Stateful is allowed",
(stateless != null && stateful != null));
}
@Test
public void backwardsCompatibilityWithEjb2Interface()
throws Exception {
if (SessionBean.class.isAssignableFrom(getBeanClass())) {
assertAnnotationIsOnMethod(PreDestroy.class, "ejbRemove");
assertAnnotationIsOnMethod(PostActivate.class,
"ejbActivate");
assertAnnotationIsOnMethod(PrePassivate.class,
"ejbPassivate");
assertAnnotationIsOnMethod(PostConstruct.class,
"ejbCreate");
}
}
@Test
public void validNumberOfLifecycleAnnotations() throws Exception {
assertNumberOfAnnotations(1, PreDestroy.class);
assertNumberOfAnnotations(1, PostActivate.class);
assertNumberOfAnnotations(1, PrePassivate.class);
assertNumberOfAnnotations(1, PostConstruct.class);
}
private void assertNumberOfAnnotations(int maxOccurrences,
Class<? extends Annotation> annotation) {
int count = countMethodsWithAnnotation(annotation);
assertTrue("@" + annotation.getName()
+ " should occur at most " + maxOccurrences
+ " times (occurred " + count + " times for "
+ getBeanClass().getName() + ")",
count <= maxOccurrences);
}
private Method getMethodWithAnnotation(
Class<? extends Annotation> annotation) {
for (Method method : getBeanClass().getMethods()) {
if (methodHasAnnotation(method, annotation)) {
return method;
}
}
return null;
}
private int countMethodsWithAnnotation(
Class<? extends Annotation> annotation) {
int count = 0;
for (Method method : getBeanClass().getMethods()) {
if (methodHasAnnotation(method, annotation)) {
count++;
}
}
return count;
}
private void assertAnnotationIsOnMethod(
Class<? extends Annotation> annotation, String ejb2Method)
throws Exception {
if (!beanClassDefinesMethod(ejb2Method)) {
return;
}
Method method = getMethodWithAnnotation(annotation);
if (method == null) {
return;
}
assertEquals("The @" + annotation.getName()
+ " annotation can only be applied to the "
+ ejb2Method
+ " method if the bean class implements "
+ SessionBean.class.getName(), ejb2Method, method
.getName());
}
private boolean methodHasAnnotation(Method actual,
Class<? extends Annotation> annotation) {
return actual.getAnnotation(annotation) != null;
}
private boolean beanClassDefinesMethod(String methodName) {
try {
getBeanClass().getMethod(methodName, new Class[0]);
return true;
} catch (NoSuchMethodException e) {
return false;
}
}
}