Deadlock in EJB due to transactions?
normann Nov 6, 2001 8:06 AMI'm working on an EJB system to be deployed under JBoss (2.4.3), but I'm running into deadlocks in the beans I'm developing. I have created an example and included it below.
The problem is that I sometimes end up in the same method in the same entity bean twice during a large transaction. But the second time I try to update on the entity bean, JBoss locks the transaction. What am I doing wrong? I have tried to change the <trans-attribute> in the deployment descriptor but that doesn't help.
Your help will be appreciated.
Best regards,
Jan Nielsen
Here's the bean class:
------------------------------------------------------
[pre]package ejb;
import javax.rmi.*;
import java.rmi.*;
import javax.ejb.*;
public class ABean implements javax.ejb.EntityBean {
private EntityContext ctx = null;
public Integer key = null;
public java.lang.String a1 = null;
public java.lang.String a2 = null;
public ABean() { super(); }
public Integer ejbCreate(Integer key) throws CreateException {
this.key = key;
this.a1 = null;
this.a2 = null;
return this.getKey();
}
public void ejbPostCreate(Integer key) {}
public void ejbLoad() {}
public void ejbStore() {}
public void ejbActivate() {}
public void ejbPassivate() {}
public void ejbRemove() throws RemoveException {}
public EntityContext getEntityContext() { return this.ctx; }
public void unsetEntityContext() { this.ctx = null; }
public void setEntityContext(EntityContext ctx) { this.ctx = ctx; }
public Integer getKey() { return this.key; }
public void setKey(Integer key) { this.key = key; }
public java.lang.String getA1() { return this.a1; }
public void setA1(java.lang.String a1) { this.a1 = a1; }
public java.lang.String getA2() { return this.a2; }
public void setA2(java.lang.String a2) {
try {
this.a2 = a2;
A a = (A) this.getEntityContext().getEJBObject();
// This method call will create a dead-lock, even though it is a
// call to the same entity bean that I'm already in. Why is that?
// The line could be replaced with this.setA1(a2) and work fine.
a.setA1(a2);
} catch (RemoteException e) {
throw new EJBException(e);
}
}
}[/pre]
------------------------------------------------------
Here's the remote interface:
------------------------------------------------------
[pre]package ejb;
import java.rmi.*;
import javax.ejb.*;
import java.util.*;
public interface A extends EJBObject {
java.lang.String getA1() throws RemoteException;
void setA1(java.lang.String a1) throws RemoteException;
java.lang.String getA2() throws RemoteException;
void setA2(java.lang.String a2) throws RemoteException;
}[/pre]
------------------------------------------------------
Here's the home interface:
------------------------------------------------------
[pre]package ejb;
import java.rmi.*;
import javax.ejb.*;
import java.util.*;
public interface AHome extends EJBHome {
A create(Integer key) throws RemoteException, CreateException;
A findByPrimaryKey(Integer key) throws RemoteException, FinderException;
}[/pre]
------------------------------------------------------
Here's the deployment descriptor:
------------------------------------------------------
[pre]<?xml version="1.0"?>
<ejb-jar>
<enterprise-beans>
<ejb-name>A</ejb-name>
ejb.AHome
ejb.A
<ejb-class>ejb.ABean</ejb-class>
<persistence-type>Container</persistence-type>
<prim-key-class>java.lang.Integer</prim-key-class>
False
<primkey-field>key</primkey-field>
<cmp-field><field-name>key</field-name></cmp-field>
<cmp-field><field-name>a1</field-name></cmp-field>
<cmp-field><field-name>a2</field-name></cmp-field>
</enterprise-beans>
<assembly-descriptor>
<container-transaction>
<ejb-name>A</ejb-name>
<method-name>*</method-name>
<trans-attribute>Required</trans-attribute>
</container-transaction>
</assembly-descriptor>
</ejb-jar>[/pre]
------------------------------------------------------
Here's my test client:
------------------------------------------------------
[pre]package ejb;
import java.rmi.*;
import javax.rmi.*;
import javax.ejb.*;
import javax.naming.*;
public class ATester {
private ATester() {
}
public static final void main(String args[]) {
new ATester().run();
}
private static AHome getAHome() {
try {
InitialContext ctx = new InitialContext();
return (AHome) PortableRemoteObject.narrow(ctx.lookup("A"), AHome.class);
} catch (NamingException e) {
System.out.println(e.toString());
return null;
}
}
private void run() {
try {
A a = this.getAHome().create(new Integer(0));
a.setA1("test"); // ok
a.setA2("test2"); // dead-lock - see remarks in bean class.
} catch (RemoteException e) {
e.printStackTrace();
} catch (CreateException e) {
e.printStackTrace();
}
}
}[/pre]