JTA Transaction problem with remote JBoss instance
nvp_saradhi Jul 16, 2004 6:41 AMHi All,
I am facing the problem with transactions across remote JBoss servers. The scenario is like this:
I have a SLSB deployed on JBoss server A which is running on machine X and I have an Entity Bean deployed on JBoss server B which is running on machine Y. Now I am invoking one of the business methods on SLSB using the client program on machine X and which in turn look up the BMP Entity Bean on machine Y and inserts a row into the database. And the session bean inserts a row into the database on machine X with the same info. of the created row in database on machine Y.
I am using the JTA API in Bean Managed SLSB to manage these database updates atomically. The problem is after the SLSB invoking the Entity Bean's create method, a new row is inserted into the database on machine X, but the JBoss server A throws the following exception and rolled back the transaction on machine Y's database.
[TxInterceptorBMT] Application error: BMT stateless bean TestEJB should complete transactions before returning (ejb1.1 spec, 11.6.1)
Please find the files I am using for reference:
SLSB code:
-------------
public int insertRow(String id) {
Connection conn = null;
UserTransaction ut = null;
PreparedStatement pst = null;
ResultSet rs = null;
int result = 0;
try {
Properties prop = new Properties();
prop.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
prop.setProperty(Context.PROVIDER_URL, "jnp://remote-host:1099");
initCtx = new InitialContext(prop);
ut = sessionCtx.getUserTransaction();
ut.begin();
AccountHome accHome = (AccountHome) PortableRemoteObject.narrow(initCtx.lookup("MyAccountHome"), AccountHome.class);
Account account = accHome.create(id, 300);
double amount = account.balance();
conn = getConnection();
pst = conn.prepareStatement("insert into test values(?,?)");
pst.setString(1, id);
pst.setDouble(2, amount);
result = pst.executeUpdate();
pst.close();
conn.close();
ut.commit();
}
catch(Exception e) {
try {
pst.close();
conn.close();
ut.rollback();
}
catch(SystemException se) {
throw new EJBException("Rollback failed: " + se.getMessage());
}
catch(SQLException se) {
throw new EJBException("Rollback failed: " + se.getMessage());
}
throw new EJBException("Transaction failed: " + e.getMessage());
}
return result;
}
ejb-jar.xml:
--------------
<?xml version="1.0"?>
<!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN" "http://java.sun.com/dtd/ejb-jar_2_0.dtd">
<ejb-jar>
<enterprise-beans>
<ejb-name>TestEJB</ejb-name>
com.abc.examples.TestHome
com.abc.examples.Test
<ejb-class>com.abc.examples.TestBean</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Bean</transaction-type>
</enterprise-beans>
</ejb-jar>
jboss.xml:
------------
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE jboss PUBLIC "-//JBoss//DTD JBOSS 3.2//EN" "http://www.jboss.org/j2ee/dtd/jboss_3_2.dtd">
<enterprise-beans>
<ejb-name>TestEJB</ejb-name>
<jndi-name>MyTestHome</jndi-name>
</enterprise-beans>
Entity Bean code:
--------------------
public String ejbCreate (String accountId, double initialBalance) throws CreateException {
this.accountId = accountId;
this.balance = initialBalance;
Connection con = null;
PreparedStatement ps = null;
try {
con = getConnection();
ps = con.prepareStatement("insert into ejbAccounts (id, bal) values(?, ?)");
ps.setString(1, accountId);
ps.setDouble(2, balance);
if (ps.executeUpdate() != 1) {
throw new CreateException ("JDBC did not create any row");
}
return accountId;
}
catch (SQLException sqe) {
try {
ejbFindByPrimaryKey(accountId);
}
catch(ObjectNotFoundException onfe) {
throw new CreateException ("SQLException: " + sqe);
}
throw new DuplicateKeyException("An Account already exists in the database with Primary Key " + accountId);
}
finally {
cleanup(con, ps);
}
}
private Connection getConnection() throws SQLException {
InitialContext initCtx = null;
try {
InitialContext ctx = new InitialContext();
DataSource ds = (javax.sql.DataSource) ctx.lookup("java:/DefaultDS");
return ds.getConnection();
} catch(NamingException ne) {
throw new EJBException(ne);
}
finally {
try {
if(initCtx != null) initCtx.close();
}
catch(NamingException ne) {
throw new EJBException("Error closing context: " + ne);
}
}
}
ejb-jar.xml:
--------------
<?xml version="1.0"?>
<!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN" "http://java.sun.com/dtd/ejb-jar_2_0.dtd">
<ejb-jar>
<enterprise-beans>
<ejb-name>beanManaged</ejb-name> com.abc.transaction.beanmanaged.AccountHomecom.abc.transaction.beanmanaged.Account
<ejb-class>com.abc.transaction.beanmanaged.AccountBean</ejb-class>
<persistence-type>Bean</persistence-type>
<prim-key-class>java.lang.String</prim-key-class>
False
<resource-ref>
<res-ref-name>jdbc/DefaultDS</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
</enterprise-beans>
<assembly-descriptor>
<container-transaction>
<ejb-name>beanManaged</ejb-name>
<method-name>*</method-name>
<trans-attribute>Required</trans-attribute>
</container-transaction>
</assembly-descriptor>
</ejb-jar>
How could I solve this problem? Any help is greatly appreciated as I am in the middle of the project. Thanks in advance.