Hi all,
Session Synchronization[4.3.7] does not seem complete in 3.2.x
- Methods in a @Stateful@LocalBean annotated with @AfterBegin does not get
called at all.
- For a @Stateful@LocalBean implementing SessionSynchronization afterBegin()
gets called, but not if it is defined in a super class.
(Not even if I redefine the methods again in the subclass)
(Notifications in superclasses works fine for a @Stateful@Local)
This is a problem a colleague have been battling with for way too long.
I think finally found exactly what was causing us this grief.
I don't see a workaround to be able to define these listeners in the super
class
and still have them called for the subclasses.
Attached is a testcase showing the issues.
It was modified from the testing-transactions example (you can copy this
test in there).
[4.3.7] The Optional Session Synchronization Notifications for Stateful
Session Beans
A stateful session bean class can optionally implement the
javax.ejb.SessionSynchroniza-
tion interface or annotate methods using the individual @AfterBegin,
@BeforeCompletion,
and @AfterCompletion annotations. The deployment descriptor may also be used
to declare the
individual session synchronization methods. These provide the session bean
instances with transaction
synchronization notifications. The instances can use these notifications,
for example, to manage data-
base data they may cache within transactions—e.g., if the Java Persistence
API is not used. A stateful
session bean class may use either the javax.ejb.SessionSynchronization
interface OR the
session synchronization annotations, but not both.
The afterBegin notification signals a session bean instance that a new
transaction has begun. The
container invokes this method before the first business method within a
transaction (which is not neces-
sarily at the beginning of the transaction). The afterBegin notification is
invoked with the transac-
tion context. The instance may do any database work it requires within the
scope of the transaction.
The beforeCompletion notification is issued when a session bean instance’s
client has completed
work on its current transaction but prior to committing the resource
managers used by the instance. At
this time, the instance should write out any database updates it has cached.
The instance can cause the
transaction to roll back by invoking the setRollbackOnly method on its
session context.
The afterCompletion notification signals that the current transaction has
completed. A completion
status of true indicates that the transaction has committed. A status of
false indicates that a rollback
has occurred. Since a session bean instance’s conversational state is not
transactional, it may need to
manually reset its state if a rollback occurred.
All container providers must support the session synchronization
notifications. It is optional only for the
bean implementor. If a bean class implements the SessionSynchronization
interface, the con-
tainer must invoke the afterBegin, beforeCompletion, and afterCompletion
notifica-
tions as required by the specification. If the bean implementor uses the
session synchronization
annotations, the container must invoke only the notifications corresponding
to the annotations that have
been used.
Protocol Between a Session Bean Instance and its ContainerEnterprise
JavaBeans 3.1, Final ReleaseSession Bean Component Contract
A session synchronization method can have public, private, protected, or
package level
access. A session synchronization method must not be declared as final or
static.
Only a stateful session bean with container-managed transaction demarcation
can receive SessionSyn-
chronization notifications. Stateless session beans and Singleton session
beans must not implement the
SessionSynchronization interface or use the session synchronization
annotations.
There is no need for a session bean with bean-managed transaction
demarcation to rely on the synchro-
nization call backs because the bean is in control of the commit—the bean
knows when the transaction
is about to be committed and it knows the outcome of the transaction commit.
--
<>< Marius ><>
package org.superbiz.injection.tx;
import java.rmi.RemoteException;
import java.util.Properties;
import javax.ejb.AfterBegin;
import javax.ejb.AfterCompletion;
import javax.ejb.BeforeCompletion;
import javax.ejb.EJBException;
import javax.ejb.Local;
import javax.ejb.LocalBean;
import javax.ejb.SessionSynchronization;
import javax.ejb.Stateful;
import javax.naming.Context;
import javax.naming.InitialContext;
import junit.framework.TestCase;
public class TestSessionNotifications extends TestCase {
private Context context;
protected void setUp() throws Exception {
Properties p = new Properties();
p.put(Context.INITIAL_CONTEXT_FACTORY,
"org.apache.openejb.client.LocalInitialContextFactory");
context = new InitialContext(p);
}
@Local
public static interface Statable {
String getState();
}
@Stateful
public static class ImplementsSessionSynchronizationL
implements Statable, SessionSynchronization {
protected String state = "initial";
public String getState() {
return state;
}
@AfterBegin
public void afterBegin() throws EJBException {
state = "afterBegin";
}
@BeforeCompletion
public void beforeCompletion() throws EJBException, RemoteException {
}
@AfterCompletion
public void afterCompletion(boolean committed) throws EJBException,
RemoteException {
}
}
public void testImplementsSessionSynchronizationL() throws Exception {
Statable x =
(Statable) context
.lookup("ImplementsSessionSynchronizationLLocal");
// this works
assertEquals("afterBegin", x.getState());
}
@Stateful
public static class ExtendedIssL
extends ImplementsSessionSynchronizationL {
}
public void testExtendedIssL() throws Exception {
Statable x =
(Statable) context
.lookup("ExtendedIssLLocal");
// this works
assertEquals("afterBegin", x.getState());
}
@LocalBean
@Stateful
public static class ImplementsSessionSynchronizationLB
implements SessionSynchronization {
protected String state = "initial";
public String getState() {
return state;
}
public void afterBegin() throws EJBException {
state = "afterBegin";
}
public void beforeCompletion() throws EJBException, RemoteException {
}
public void afterCompletion(boolean committed) throws EJBException,
RemoteException {
}
}
public void testImplementsSessionSynchronizationLB() throws Exception {
ImplementsSessionSynchronizationLB x =
(ImplementsSessionSynchronizationLB) context
.lookup("ImplementsSessionSynchronizationLBLocalBean");
// this works
assertEquals("afterBegin", x.getState());
}
@LocalBean
@Stateful
public static class ExtendedIssLB
extends ImplementsSessionSynchronizationLB {
}
public void testExtendedIssLB() throws Exception {
ExtendedIssLB x =
(ExtendedIssLB) context
.lookup("ExtendedIssLBLocalBean");
// this fails
assertEquals("afterBegin", x.getState());
}
@LocalBean
@Stateful
public static class ExtendedIssOverrideAgainLB
extends ImplementsSessionSynchronizationLB
implements SessionSynchronization {
public void afterBegin() throws EJBException {
super.afterBegin();
}
public void beforeCompletion() throws EJBException, RemoteException {
super.beforeCompletion();
}
public void afterCompletion(boolean committed) throws EJBException,
RemoteException {
super.afterCompletion(committed);
}
}
public void testExtendedIssOverrideAgainLB() throws Exception {
ExtendedIssOverrideAgainLB x =
(ExtendedIssOverrideAgainLB) context
.lookup("ExtendedIssOverrideAgainLBLocalBean");
// this fails
assertEquals("afterBegin", x.getState());
}
@LocalBean
@Stateful
public static class JustAnnoated {
protected String state = "initial";
public String getState() {
return state;
}
@AfterBegin
public void afterBegin() throws EJBException {
state = "afterBegin";
}
}
public void testJustAnnoated() throws Exception {
JustAnnoated x = (JustAnnoated) context
.lookup("JustAnnoatedLocalBean");
// this fails
assertEquals("afterBegin", x.getState());
}
@LocalBean
@Stateful
public static class ExtendedJa
extends JustAnnoated {
}
public void testExtendedJa() throws Exception {
ExtendedJa x =
(ExtendedJa) context
.lookup("ExtendedJaLocalBean");
// this fails
assertEquals("afterBegin", x.getState());
}
}