1 Reply Latest reply on Jan 6, 2002 2:50 AM by Sven Welte

    JBoss 2.4.4 bug - deadlock with two beans

    Donatas Ciuksys Newbie

      It seems to be a JBoss 2.4.4 bug. I have following tables in PostgreSQL DB:

      CREATE SEQUENCE Type_ID_seq;
      CREATE TABLE Type (
      ID INTEGER DEFAULT nextval('Type_ID_seq') NOT NULL,
      Description VARCHAR(30) NOT NULL,
      CONSTRAINT PK_Type PRIMARY KEY (ID),
      );

      CREATE SEQUENCE Category_ID_seq;
      CREATE TABLE Category (
      ID INTEGER DEFAULT nextval('Category_ID_seq') NOT NULL,
      Description VARCHAR(30) NOT NULL,
      Type_ID INTEGER NOT NULL,
      CONSTRAINT PK_Categ PRIMARY KEY (ID),
      CONSTRAINT FK_Categ_Type FOREIGN KEY (Type_ID) REFERENCES Type (ID)
      );

      I have two local entity BMP beans - TypeBean and CategoryBean that work with these tables. Also I have one remote session bean (OSISManagerBean) that allows user to access tables. For both Type and Category beans I have value objects: TypeInfo and CategoryInfo. Entity beans do not have references to each other, they are not reentrant, but following client code deadlocks during first call to CategoryBean.ejbStore() (though it works OK with Sun j2eesdk):

      public class Client {

      public static void main(String[] args) {
      try {
      Hashtable env = new Hashtable();
      env.put("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory");
      env.put("java.naming.provider.url", "jnp://193.219.42.29:1099");
      env.put("java.naming.factory.url.pkgs", "org.jboss.naming:org.jnp.interfaces");
      Context initial = new InitialContext(env);
      Object objref = initial.lookup("ejb/OSISManagerBean");

      OSISManagerHome home = (OSISManagerHome)PortableRemoteObject.narrow(objref, OSISManagerHome.class);

      OSISManager manager = home.create();
      Collection types = manager.getAllTypes();
      Iterator iterator = types.iterator();
      while (iterator.hasNext()) {
      TypeInfo typeInfo = (TypeInfo)iterator.next();
      System.out.println("TypeInfo: id = " + typeInfo.getId() + ", description = " + typeInfo.getDescription());

      Collection categories = manager.getCategoriesByType(typeInfo.getId().intValue());
      Iterator iterator2 = categories.iterator();
      while (iterator2.hasNext()) {
      CategoryInfo categoryInfo = (CategoryInfo)iterator2.next();
      System.out.println(" CategoryInfo: " + categoryInfo);
      }
      }
      } catch (Exception ex) {
      System.err.println("Caught an unexpected exception!");
      ex.printStackTrace();
      }
      }
      }

      Here is the source code for all the classes:

      -----------------------------------------------------------
      import javax.ejb.EJBLocalObject;

      public interface Type extends EJBLocalObject {

      public TypeInfo getTypeInfo();
      public void change(TypeInfo typeInfo);

      }
      -----------------------------------------------------------
      public class TypeInfo implements java.io.Serializable {
      private Integer m_id;
      private String m_description;

      public TypeInfo(String description) {
      m_id = null;
      m_description = description;
      }

      public void setId(int id) {
      m_id = new Integer(id);
      }

      public Integer getId() {
      return m_id;
      }

      public String getDescription() {
      return m_description;
      }

      public String toString() {
      return "ID = " + m_id + ", description = " + m_description;
      }
      }
      -----------------------------------------------------------
      import javax.ejb.*;
      import java.util.Collection;

      public interface TypeHome extends EJBLocalHome {

      public Type create(TypeInfo typeInfo) throws CreateException;

      public Type findByPrimaryKey(Integer id) throws FinderException;
      public Collection findAll() throws FinderException;
      }
      -----------------------------------------------------------
      import lt.likit.opensourceis.ejb.JNDINames;

      import javax.ejb.*;
      import javax.naming.NamingException;
      import javax.naming.InitialContext;
      import javax.sql.DataSource;
      import java.sql.*;
      import java.util.Collection;
      import java.util.Vector;

      public class TypeBean implements EntityBean {

      private Integer m_id;
      private TypeInfo m_typeInfo;
      private EntityContext m_context;
      private Connection m_connection;

      public TypeInfo getTypeInfo() {
      return m_typeInfo;
      }

      public void change(TypeInfo typeInfo) {
      typeInfo.setId(m_id.intValue());
      m_typeInfo = typeInfo;
      }

      public Integer ejbCreate(TypeInfo typeInfo) throws CreateException {
      int id;
      try {
      id = insertRow(typeInfo);
      } catch (Exception ex) {
      throw new EJBException("TypeBean.ejbCreate(): " + ex.getMessage());
      }

      m_id = new Integer(id);
      typeInfo.setId(id);
      m_typeInfo = typeInfo;

      return m_id;
      }

      public void ejbPostCreate(TypeInfo typeInfo) {
      }

      public Integer ejbFindByPrimaryKey(Integer primaryKey) throws FinderException {
      boolean result;
      try {
      result = selectByPrimaryKey(primaryKey.intValue());
      } catch (Exception ex) {
      throw new EJBException("TypeBean.ejbFindByPrimaryKey(): " + ex.getMessage());
      }
      if (result) {
      return primaryKey;
      } else {
      throw new ObjectNotFoundException("TypeBean.ejbFindByPrimaryKey(): Row for id " + primaryKey + " not found.");
      }
      }

      public Collection ejbFindAll() throws FinderException {
      try {
      return selectAll();
      } catch (Exception e) {
      throw new EJBException("TypeBean.ejbFindAll(): " + e.getMessage());
      }
      }

      public void ejbRemove() {
      try {
      deleteRow(m_id.intValue());
      } catch (Exception ex) {
      throw new EJBException("TypeBean.ejbRemove(): " + ex.getMessage());
      }
      }

      public void setEntityContext(EntityContext context) {
      m_context = context;
      try {
      makeConnection();
      } catch (Exception ex) {
      throw new EJBException("TypeBean.setEntityContext(): Unable to connect to database. " + ex.getMessage());
      }
      }

      public void unsetEntityContext() {
      try {
      m_connection.close();
      } catch (SQLException ex) {
      throw new EJBException("TypeBean.unsetEntityContext(): " + ex.getMessage());
      }
      }

      public void ejbActivate() {
      m_id = (Integer)m_context.getPrimaryKey();
      }

      public void ejbPassivate() {
      m_id = null;
      }

      public void ejbLoad() {
      try {
      System.out.println("TypeBean[ID=" + m_id + "]: ejbLoad");
      loadRow();
      } catch (Exception ex) {
      throw new EJBException("TypeBean.ejbLoad(): " + ex.getMessage());
      }
      }

      public void ejbStore() {
      try {
      System.out.println("TypeBean[ID=" + m_id + "]: ejbStore");
      storeRow();
      } catch (Exception ex) {
      throw new EJBException("TypeBean.ejbStore(): " + ex.getMessage());
      }
      }

      /*********************** Database Routines *************************/

      private void makeConnection() throws NamingException, SQLException {
      InitialContext ic = new InitialContext();
      DataSource ds = (DataSource)ic.lookup(JNDINames.DB_NAME);
      m_connection = ds.getConnection();
      }

      private int insertRow(TypeInfo typeInfo) throws SQLException {
      Statement statement = m_connection.createStatement();
      ResultSet rs = statement.executeQuery("select nextval('Type_ID_seq')");
      int newId;
      if (rs.next()) {
      newId = rs.getInt(1);
      statement.close();
      } else {
      statement.close();
      throw new EJBException("TypeBean.insertRow(): Function nextval('Type_ID_seq') has not returned a value.");
      }

      String insertStatement = "insert into TYPE (id, description) values ( ? , ? )";
      PreparedStatement prepStmt = m_connection.prepareStatement(insertStatement);
      prepStmt.setInt(1, newId);
      prepStmt.setString(2, typeInfo.getDescription());
      prepStmt.executeUpdate();
      prepStmt.close();

      return newId;
      }

      private void deleteRow(int id) throws SQLException {
      String deleteStatement = "delete from TYPE where id = ?";
      PreparedStatement prepStmt = m_connection.prepareStatement(deleteStatement);
      prepStmt.setInt(1, id);
      prepStmt.executeUpdate();
      prepStmt.close();
      }

      private boolean selectByPrimaryKey(int primaryKey) throws SQLException {
      String selectStatement = "select id from TYPE where id = ?";
      PreparedStatement prepStmt = m_connection.prepareStatement(selectStatement);
      prepStmt.setInt(1, primaryKey);

      ResultSet rs = prepStmt.executeQuery();
      boolean result = rs.next();
      prepStmt.close();
      return result;
      }

      private Collection selectAll() throws SQLException {
      Statement statement = m_connection.createStatement();
      ResultSet rs = statement.executeQuery("select id from TYPE");

      Collection collection = new Vector();
      while (rs.next()) {
      int id = rs.getInt(1);
      collection.add(new Integer(id));
      }
      statement.close();

      return collection;
      }

      private void loadRow() throws SQLException {
      String selectStatement = "select description from TYPE where id = ?";
      PreparedStatement prepStmt = m_connection.prepareStatement(selectStatement);
      prepStmt.setInt(1, m_id.intValue());

      ResultSet rs = prepStmt.executeQuery();

      if (rs.next()) {
      String description = rs.getString(1);
      m_typeInfo = new TypeInfo(description);
      m_typeInfo.setId(m_id.intValue());
      prepStmt.close();
      } else {
      prepStmt.close();
      throw new NoSuchEntityException("TypeBean.loadRow(): Row for id " + m_id + " not found in database.");
      }
      }

      private void storeRow() throws SQLException {
      String updateStatement = "update TYPE set description = ? where id = ?";
      PreparedStatement prepStmt = m_connection.prepareStatement(updateStatement);

      prepStmt.setString(1, m_typeInfo.getDescription());
      prepStmt.setInt(2, m_id.intValue());
      int rowCount = prepStmt.executeUpdate();
      prepStmt.close();

      if (rowCount == 0) {
      throw new EJBException("TypeBean.storeRow(): Storing row for id " + m_id + " failed.");
      }
      }
      }
      -----------------------------------------------------------
      import javax.ejb.EJBLocalObject;

      public interface Category extends EJBLocalObject {

      public CategoryInfo getCategoryInfo();

      }
      -----------------------------------------------------------
      public class CategoryInfo implements java.io.Serializable {

      private Integer m_id;
      private String m_description;
      private int m_typeId;

      public CategoryInfo(String description) {
      m_id = null;
      m_description = description;
      m_typeInfo = null;
      }

      public Integer getId() {
      return m_id;
      }

      public void setId(int id) {
      m_id = new Integer(id);
      }

      public String getDescription() {
      return m_description;
      }

      public int getTypeId() {
      return m_typeId;
      }

      public void setTypeId(int typeId) {
      m_typeId = typeId;
      }

      public String toString() {
      return "[id=" + m_id + ", description=" + m_description + ", typeId=" + m_typeId + "]";
      }
      }
      -----------------------------------------------------------
      import javax.ejb.EJBLocalHome;
      import javax.ejb.CreateException;
      import javax.ejb.FinderException;
      import java.util.Collection;

      public interface CategoryHome extends EJBLocalHome {

      public Category create(CategoryInfo categoryInfo, int typeId) throws CreateException, OpenSourceISException;
      public Category findByPrimaryKey(Integer id) throws FinderException;
      public Collection findByType(int typeId) throws FinderException;
      }
      -----------------------------------------------------------
      package lt.likit.opensourceis.ejb.category;

      import lt.likit.opensourceis.ejb.type.TypeInfo;
      import lt.likit.opensourceis.ejb.type.TypeHome;
      import lt.likit.opensourceis.ejb.type.Type;
      import lt.likit.opensourceis.ejb.JNDINames;
      import lt.likit.opensourceis.ejb.manager.OSISManagerBean;
      import lt.likit.opensourceis.exception.OpenSourceISException;

      import javax.ejb.*;
      import javax.naming.NamingException;
      import javax.naming.InitialContext;
      import javax.naming.Context;
      import javax.sql.DataSource;
      import java.sql.*;
      import java.util.Collection;
      import java.util.Vector;

      /**
      * Category Bean
      */
      public class CategoryBean implements EntityBean {

      private Integer m_id;
      private CategoryInfo m_categoryInfo;
      private EntityContext m_context;
      private Connection m_connection;

      public CategoryInfo getCategoryInfo() {
      return m_categoryInfo;
      }

      public Integer ejbCreate(CategoryInfo categoryInfo, int typeId) throws CreateException, OpenSourceISException {
      int id;
      try {
      id = insertRow(categoryInfo, typeId);
      } catch (Exception ex) {
      throw new EJBException("TypeBean.ejbCreate(): " + ex.getMessage());
      }

      m_id = new Integer(id);
      categoryInfo.setId(id);
      m_categoryInfo = categoryInfo;
      m_categoryInfo.setTypeId(typeId);

      return m_id;
      }

      public void ejbPostCreate(CategoryInfo categoryInfo, int typeId) {
      }

      public Integer ejbFindByPrimaryKey(Integer primaryKey) throws FinderException {
      boolean result;
      try {
      result = selectByPrimaryKey(primaryKey.intValue());
      } catch (Exception ex) {
      throw new EJBException("CategoryBean.ejbFindByPrimaryKey(): " + ex.getMessage());
      }
      if (result) {
      return primaryKey;
      } else {
      throw new ObjectNotFoundException("CategoryBean.ejbFindByPrimaryKey(): Row for id " + primaryKey + " not found.");
      }
      }

      public Collection ejbFindByType(int typeId) throws FinderException {
      try {
      return selectByType(typeId);
      } catch (Exception e) {
      throw new EJBException("CategoryBean.ejbFindByType(): " + e.getMessage());
      }
      }

      public void ejbRemove() {
      try {
      deleteRow(m_id.intValue());
      } catch (Exception ex) {
      throw new EJBException("CategoryBean.ejbRemove(): " + ex.getMessage());
      }
      }

      public void setEntityContext(EntityContext context) {
      m_context = context;
      try {
      makeConnection();
      } catch (Exception ex) {
      throw new EJBException("CategoryBean.setEntityContext(): Unable to connect to database. " + ex.getMessage());
      }
      }

      public void unsetEntityContext() {
      try {
      m_connection.close();
      } catch (SQLException ex) {
      throw new EJBException("CategoryBean.unsetEntityContext(): " + ex.getMessage());
      }
      }

      public void ejbActivate() {
      m_id = (Integer)m_context.getPrimaryKey();
      }

      public void ejbPassivate() {
      m_id = null;
      }

      public void ejbLoad() {
      try {
      System.out.print("CategoryBean[id="+m_id+"]: loading row...");
      loadRow();
      System.out.println("Done.");
      } catch (Exception ex) {
      throw new EJBException("CategoryBean.ejbLoad(): " + ex.getMessage());
      }
      }

      public void ejbStore() {
      try {
      storeRow();
      } catch (Exception ex) {
      throw new EJBException("CategoryBean.ejbStore(): " + ex.getMessage());
      }
      }

      /*********************** Database Routines *************************/

      private void makeConnection() throws NamingException, SQLException {
      InitialContext ic = new InitialContext();
      DataSource ds = (DataSource)ic.lookup(JNDINames.DB_NAME);
      m_connection = ds.getConnection();
      }

      private int insertRow(CategoryInfo categoryInfo, int typeId) throws SQLException {
      Statement statement = m_connection.createStatement();
      ResultSet rs = statement.executeQuery("select nextval('Category_ID_seq')");
      int newId;
      if (rs.next()) {
      newId = rs.getInt(1);
      statement.close();
      } else {
      statement.close();
      throw new EJBException("CategoryBean.insertRow(): Function nextval('Category_ID_seq') has not returned a value.");
      }

      String insertStatement = "insert into CATEGORY (id, description, type_id) values ( ? , ? , ? )";
      PreparedStatement prepStmt = m_connection.prepareStatement(insertStatement);
      prepStmt.setInt(1, newId);
      prepStmt.setString(2, categoryInfo.getDescription());
      prepStmt.setInt(3, typeId);
      prepStmt.executeUpdate();
      prepStmt.close();

      return newId;
      }

      private void deleteRow(int id) throws SQLException {
      String deleteStatement = "delete from CATEGORY where id = ?";
      PreparedStatement prepStmt = m_connection.prepareStatement(deleteStatement);
      prepStmt.setInt(1, id);
      prepStmt.executeUpdate();
      prepStmt.close();
      }

      private boolean selectByPrimaryKey(int primaryKey) throws SQLException {
      String selectStatement = "select id from CATEGORY where id = ?";
      PreparedStatement prepStmt = m_connection.prepareStatement(selectStatement);
      prepStmt.setInt(1, primaryKey);

      ResultSet rs = prepStmt.executeQuery();
      boolean result = rs.next();
      prepStmt.close();
      return result;
      }

      private Collection selectByType(int typeId) throws SQLException {
      String selectStatement = "select id from CATEGORY where type_id = ?";
      PreparedStatement prepStmt = m_connection.prepareStatement(selectStatement);
      prepStmt.setInt(1, typeId);

      ResultSet rs = prepStmt.executeQuery();
      Collection collection = new Vector();
      while (rs.next()) {
      int id = rs.getInt(1);
      collection.add(new Integer(id));
      }
      prepStmt.close();

      return collection;
      }

      private void loadRow() throws SQLException, OpenSourceISException {
      String selectStatement = "select description, type_id from CATEGORY where id = ?";
      PreparedStatement prepStmt = m_connection.prepareStatement(selectStatement);
      prepStmt.setInt(1, m_id.intValue());

      ResultSet rs = prepStmt.executeQuery();

      if (rs.next()) {
      String description = rs.getString(1);
      int typeId = rs.getInt(2);
      prepStmt.close();

      m_categoryInfo = new CategoryInfo(description);
      m_categoryInfo.setId(m_id.intValue());
      m_categoryInfo.setTypeId(typeId);
      } else {
      prepStmt.close();
      throw new NoSuchEntityException("CategoryBean.loadRow(): Row for id " + m_id + " not found in database.");
      }
      }

      private void storeRow() throws SQLException {
      String updateStatement = "update CATEGORY set description = ?, type_id = ? where id = ?";
      PreparedStatement prepStmt = m_connection.prepareStatement(updateStatement);

      prepStmt.setString(1, m_categoryInfo.getDescription());
      prepStmt.setInt(2, m_categoryInfo.getTypeInfo().getId().intValue());
      prepStmt.setInt(3, m_id.intValue());
      System.out.print("CategoryBean[id="+m_id+"]: executing executeUpdate()...");
      int rowCount = prepStmt.executeUpdate();
      System.out.println("Done.");
      prepStmt.close();

      if (rowCount == 0) {
      throw new EJBException("CategoryBean.storeRow(): Storing row for id " + m_id + " failed.");
      }
      }
      }
      -----------------------------------------------------------
      import javax.ejb.EJBObject;
      import java.util.Collection;
      import java.util.Vector;
      import java.rmi.RemoteException;

      /**
      * OpenSource Information System Manager incapsulates all the functionality
      * and business logic needed by clients (and administrators) of this system.
      */
      public interface OSISManager extends EJBObject {
      public TypeInfo getType(int typeId) throws RemoteException, OpenSourceISException;
      public Collection getAllTypes() throws RemoteException, OpenSourceISException;

      public Collection getCategoriesByType(int typeId) throws RemoteException, OpenSourceISException;
      }
      -----------------------------------------------------------
      import javax.ejb.EJBHome;
      import javax.ejb.CreateException;
      import java.rmi.RemoteException;

      public interface OSISManagerHome extends EJBHome {
      public OSISManager create() throws RemoteException, CreateException;
      }
      -----------------------------------------------------------
      import javax.ejb.*;
      import javax.rmi.PortableRemoteObject;
      import javax.naming.InitialContext;
      import javax.naming.Context;
      import javax.transaction.UserTransaction;
      import javax.transaction.SystemException;
      import javax.transaction.Status;
      import java.util.Collection;
      import java.util.Iterator;
      import java.util.Vector;
      import java.sql.Date;

      public class OSISManagerBean implements SessionBean {

      private SessionContext m_sessionContext;
      private TypeHome m_typeHome;
      private CategoryHome m_categoryHome;

      public OSISManagerBean() {
      }

      public TypeInfo getType(int typeId) throws OpenSourceISException {
      try {
      Type type = m_typeHome.findByPrimaryKey(new Integer(typeId));
      return type.getTypeInfo();
      } catch (FinderException fe) {
      throw new OpenSourceISException("OSISManagerBean.getType(): " + fe.getMessage());
      } catch (EJBException ejbe) {
      throw new OpenSourceISException("OSISManagerBean.getType(): " + ejbe.getMessage());
      }
      }

      /**
      * Returns collection of TypeInfo objects.
      */
      public Collection getAllTypes() throws OpenSourceISException {
      System.out.println("In getAllTypes().");
      Collection types;
      try {
      types = m_typeHome.findAll();
      } catch (FinderException fe) {
      throw new OpenSourceISException("OSISManagerBean.getTypes(): " + fe.getMessage());
      } catch (EJBException ejbe) {
      throw new OpenSourceISException("OSISManagerBean.getAllTypes(): " + ejbe.getMessage());
      }

      Collection typeInfos = new Vector();
      Iterator iterator = types.iterator();
      while (iterator.hasNext()) {
      Type type = (Type)iterator.next();
      typeInfos.add(type.getTypeInfo());
      }
      System.out.println("Exiting getAllTypes().");
      return typeInfos;
      }

      public Collection getCategoriesByType(int typeId) throws OpenSourceISException {
      System.out.print("OSISManagerBean.getCategoriesByType(): looking for TypeBean[id=" + typeId + "]...");
      Type type;
      try {
      type = m_typeHome.findByPrimaryKey(new Integer(typeId));
      } catch (FinderException fe) {
      throw new OpenSourceISException("CategoryBean.resolveReferences(): " + fe.getMessage());
      }
      System.out.println("Done.");

      System.out.print("OSISManagerBean.getCategoriesByType(): getting TypeInfo...");
      TypeInfo typeInfo = type.getTypeInfo();
      System.out.println("Done.");

      Collection categories;
      try {
      categories = m_categoryHome.findByType(typeId);
      } catch (FinderException fe) {
      throw new EJBException("OSISManagerBean.getCategoriesByType(): " + fe.getMessage());
      } catch (EJBException ejbe) {
      throw new OpenSourceISException("OSISManagerBean.getCategoriesByType(): " + ejbe.getMessage());
      }

      Collection categoryInfos = new Vector();
      Iterator iterator = categories.iterator();
      while (iterator.hasNext()) {
      Category category = (Category)iterator.next();
      System.out.println("Before getCategoryInfo()");
      CategoryInfo categoryInfo = category.getCategoryInfo();
      System.out.println("Got categoryInfo = " + categoryInfo);

      categoryInfo.setTypeInfo(typeInfo);
      categoryInfos.add(categoryInfo);
      System.out.println("Added to collection.");
      }

      System.out.println("Exiting getCategoriesByType().");
      return categoryInfos;
      }

      public void ejbCreate() {
      }

      public void ejbRemove() {
      }

      public void ejbActivate() {
      }

      public void ejbPassivate() {
      }

      public void setSessionContext(SessionContext sc) {
      m_sessionContext = sc;
      try {
      Context initial = new InitialContext();

      m_typeHome = (TypeHome)initial.lookup(JNDINames.EJB_TYPE_BEAN);
      m_categoryHome = (CategoryHome)initial.lookup(JNDINames.EJB_CATEGORY_BEAN);
      } catch (Exception e) {
      throw new EJBException("OSISManagerBean.setSessionContext(): " + e.getMessage());
      }
      }
      }
      -----------------------------------------------------------

        • 1. Re: JBoss 2.4.4 bug - deadlock with two beans
          Sven Welte Newbie

          To be honest: I did not read the whole code maybe you could provide a smaller version.
          But there is something I do not like:
          Why are you getting the database connection in setEntityContext and releasing the connection in unsetEntityContext ? IIRC setEntityContext will be called right after instantiation and before the entity bean will be put in the pool. Remember you could have 10000 or more entity beans in the pool.
          So, you do not have a deadlock but the database is running out of connections.
          Try to get a connection right before a SQL-Command and release the connection as fast as possible.
          JBoss will "cache" the connection, so a second call to get a connection will be pretty fast.

          regards
          Sven