0 Replies Latest reply on Mar 26, 2007 3:01 PM by Sean Sawyer

    Cascading deletion fails intermittently due to NPE

    Sean Sawyer Newbie

      When attempting to remove a bean that has cascading deletion defined on its relationship to another bean, I sometimes receive an NPE when the removal is attempted. The relevant portion of the stack trace is as follows:

      14:03:34,448 INFO [STDOUT] com.gg.security.permissions.exception.PermissionsException: Could not delete old permissions for [FolderContainedEntityEJB: path='testperm/test1', parentPath='testperm', id='7fbc44610a0001ee01cb95b31eb23fd7']: Could not delete entity: null
      14:03:34,449 INFO [STDOUT] at com.gg.model.security.permissions.modify.PermissionsModifierImpl.deletePermissions(PermissionsModifierImpl.java:237)
      14:03:34,449 INFO [STDOUT] at com.gg.model.security.permissions.modify.PermissionsModifierImpl.ensurePermissions(PermissionsModifierImpl.java:216)
      14:03:34,449 INFO [STDOUT] at com.gg.model.security.permissions.modify.PermissionsModifierImpl.modify(PermissionsModifierImpl.java:70)
      ...
      14:03:34,460 INFO [STDOUT] Caused by: java.lang.NullPointerException
      14:03:34,460 INFO [STDOUT] at org.jboss.ejb.plugins.cmp.jdbc.JDBCStoreManager$CascadeDeleteRegistry.unschedule(JDBCStoreManager.java:752)
      14:03:34,460 INFO [STDOUT] at org.jboss.ejb.plugins.cmp.jdbc.JDBCStoreManager.unscheduledCascadeDelete(JDBCStoreManager.java:259)
      14:03:34,460 INFO [STDOUT] at org.jboss.ejb.plugins.cmp.jdbc.CascadeDeleteStrategy$BatchCascadeDeleteStrategy.cascadeDelete(CascadeDeleteStrategy.java:168)
      14:03:34,461 INFO [STDOUT] at org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCCMRFieldBridge.cascadeDelete(JDBCCMRFieldBridge.java:406)
      14:03:34,461 INFO [STDOUT] at org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCEntityBridge.cascadeDelete(JDBCEntityBridge.java:322)
      14:03:34,461 INFO [STDOUT] at org.jboss.ejb.plugins.cmp.jdbc.JDBCRemoveEntityCommand.execute(JDBCRemoveEntityCommand.java:117)
      14:03:34,461 INFO [STDOUT] at org.jboss.ejb.plugins.cmp.jdbc.JDBCStoreManager.removeEntity(JDBCStoreManager.java:682)
      14:03:34,461 INFO [STDOUT] at org.jboss.ejb.plugins.CMPPersistenceManager.removeEntity(CMPPersistenceManager.java:466)
      14:03:34,461 INFO [STDOUT] at org.jboss.resource.connectionmanager.CachedConnectionInterceptor.removeEntity(CachedConnectionInterceptor.java:457)
      14:03:34,461 INFO [STDOUT] at org.jboss.ejb.EntityContainer.remove(EntityContainer.java:500)
      14:03:34,461 INFO [STDOUT] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      14:03:34,461 INFO [STDOUT] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
      14:03:34,461 INFO [STDOUT] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
      14:03:34,461 INFO [STDOUT] at java.lang.reflect.Method.invoke(Method.java:585)
      14:03:34,461 INFO [STDOUT] at org.jboss.ejb.EntityContainer$ContainerInterceptor.invoke(EntityContainer.java:1107)
      14:03:34,461 INFO [STDOUT] at org.jboss.ejb.plugins.cmp.jdbc.JDBCRelationInterceptor.invoke(JDBCRelationInterceptor.java:72)
      14:03:34,461 INFO [STDOUT] at org.jboss.ejb.plugins.EntitySynchronizationInterceptor.invoke(EntitySynchronizationInterceptor.java:345)
      14:03:34,461 INFO [STDOUT] at org.jboss.resource.connectionmanager.CachedConnectionInterceptor.invoke(CachedConnectionInterceptor.java:186)
      14:03:34,462 INFO [STDOUT] at org.jboss.ejb.plugins.EntityReentranceInterceptor.invoke(EntityReentranceInterceptor.java:116)
      14:03:34,462 INFO [STDOUT] at org.jboss.ejb.plugins.EntityInstanceInterceptor.invoke(EntityInstanceInterceptor.java:211)
      14:03:34,462 INFO [STDOUT] at org.jboss.ejb.plugins.EntityLockInterceptor.invoke(EntityLockInterceptor.java:89)
      14:03:34,462 INFO [STDOUT] at org.jboss.ejb.plugins.EntityCreationInterceptor.invoke(EntityCreationInterceptor.java:54)
      14:03:34,462 INFO [STDOUT] at org.jboss.ejb.plugins.AbstractTxInterceptor.invokeNext(AbstractTxInterceptor.java:84)
      14:03:34,462 INFO [STDOUT] at org.jboss.ejb.plugins.TxInterceptorCMT.runWithTransactions(TxInterceptorCMT.java:343)
      14:03:34,462 INFO [STDOUT] at org.jboss.ejb.plugins.TxInterceptorCMT.invoke(TxInterceptorCMT.java:150)
      14:03:34,462 INFO [STDOUT] at org.jboss.ejb.plugins.SecurityInterceptor.invoke(SecurityInterceptor.java:111)
      14:03:34,462 INFO [STDOUT] at org.jboss.ejb.plugins.ProxyFactoryFinderInterceptor.invoke(ProxyFactoryFinderInterceptor.java:122)
      14:03:34,462 INFO [STDOUT] at org.jboss.ejb.EntityContainer.internalInvoke(EntityContainer.java:484)
      14:03:34,462 INFO [STDOUT] at org.jboss.ejb.Container.invoke(Container.java:709)
      14:03:34,472 INFO [STDOUT] at org.jboss.ejb.plugins.local.BaseLocalProxyFactory.invoke(BaseLocalProxyFactory.java:419)
      14:03:34,472 INFO [STDOUT] at org.jboss.ejb.plugins.local.EntityProxy.invoke(EntityProxy.java:44)
      14:03:34,472 INFO [STDOUT] at $Proxy271.remove(Unknown Source)
      14:03:34,472 INFO [STDOUT] at com.gg.adapter.BaseEntityAdapter.remove(BaseEntityAdapter.java:90)
      14:03:34,473 INFO [STDOUT] at com.gg.model.delete.BaseEntityDeleter.delete(BaseEntityDeleter.java:24)
      14:03:34,473 INFO [STDOUT] at com.gg.manager.remove.EntityRemoverImpl.removeEntity(EntityRemoverImpl.java:35)
      14:03:34,473 INFO [STDOUT] ... 66 more
      


      The basic scenario is that a folder in the app has some permissions, which in turn are defined by a number of "access control list entries" (aka ACLEntries). Thus, a one-to-many CMR relationship between the Permissions and ACLEntry beans, which is defined like so:

      PermissionsBean.java
      /**
       * @ejb.bean
       * name="Permissions"
       * type="CMP"
       * view-type="local"
       * local-jndi-name="ejb/Permissions"
       * reentrant="false"
       * primkey-field="id"
       * schema="Permissions"
       * transaction-type="Container"
       * cmp-version="2.x"
       *
       * @ejb.interface
       * local-class="com.gg.model.security.permissions.interfaces.Permissions"
       * local-extends="com.hannonhill.object.interfaces.BaseEntity"
       * @ejb.home
       * local-class="com.gg.model.security.permissions.interfaces.PermissionsHome"
       * local-extends="javax.ejb.EJBLocalHome"
       *
       * @ejb.pk
       * class="java.lang.String" extends="java.lang.Object"
       *
       * @jboss.persistence
       * create-table="true"
       *
       * @ejb.persistence
       * table-name="permissions"
       *
       * @ejb.util
       * generate="physical"
       *
       * @jboss.container-configuration
       * name="CMP 2.x and Cache"
       *
       * @ejb.transaction
       * type="Supports"
       *
       */
      public abstract class PermissionsBean extends BaseEntityBean implements PermissionsLevels
      {
       <!-- some unrelated ejb code -->
      
       /**
       * Returns a collection of ACLEntries this Permissions object contains.
       *
       * @ejb.relation
       * name="aclentry-has-permissions"
       * role-name="permission-aclentrys"
       * target-ejb="ACLEntry"
       * @ejb.interface-method
       * view-type="local"
       *
       * @jboss.method-attributes
       * read-only="true"
       */
       public abstract java.util.Collection getACLEntries();
      
       /**
       * sets the Collection of Destination objects for this Target.
       *
       * @ejb.interface-method
       * view-type="local"
       * @ejb.transaction
       * type="Required"
       */
       public abstract void setACLEntries(java.util.Collection aclEntries);
      
       /**
       * Deletes this entity.
       */
       public void ejbRemove() throws RemoveException
       {
       super.ejbRemove();
       }
      
       <!-- more unrelated ejb stuff -->
      }
      


      ACLEntryBean.java
      /**
       * @ejb.bean
       * name="ACLEntry"
       * type="CMP"
       * view-type="local"
       * local-jndi-name="ejb/ACLEntry"
       * reentrant="false"
       * primkey-field="id"
       * schema="ACLEntry"
       * transaction-type="Container"
       * cmp-version="2.x"
       *
       * @ejb.interface
       * local-class="com.gg.model.security.permissions.interfaces.ACLEntry"
       * local-extends="com.hannonhill.object.interfaces.BaseEntity"
       * @ejb.home
       * local-class="com.gg.security.permissions.interfaces.ACLEntryHome"
       * local-extends="javax.ejb.EJBLocalHome"
       *
       * @ejb.pk
       * class="java.lang.String" extends="java.lang.Object"
       *
       * @jboss.persistence
       * create-table="true"
       *
       * @ejb.persistence
       * table-name="aclentry"
       *
       * @ejb.util
       * generate="physical"
       *
       * @jboss.container-configuration
       * name="CMP 2.x and Cache"
       *
       * @ejb.transaction
       * type="Supports"
       *
       * @ejb.finder
       * signature="com.gg.security.permissions.interfaces.ACLEntry findByPrimaryKey(java.lang.String pk)"
       * query="SELECT OBJECT(x) FROM ACLEntry AS x WHERE x.id = ?1"
       */
      public abstract class ACLEntryBean extends BaseEntityBean
      {
       <!-- some unrelated ejb stuff -->
      
       /**
       * @ejb.relation
       * name="aclentry-has-permissions"
       * role-name="aclentry-permissions"
       * target-ejb="Permissions"
       * cascade-delete="yes"
       * @jboss.relation
       * related-pk-field="id"
       * fk-column="permissionsId"
       * batch-cascade-delete="true"
       *
       * @ejb.interface-method
       * view-type="local"
       *
       * @jboss.method-attributes
       * read-only="true"
       */
       public abstract com.gg.security.permissions.intefaces.Permissions getPermissions();
      
       /**
       * @ejb.interface-method
       * view-type="local"
       * @ejb.transaction
       * type="Required"
       */
       public abstract void setPermissions(Permissions permissions);
      
       /**
       * Vanilla ejb remove.
       */
       public void ejbRemove() throws RemoveException
       {
       super.ejbRemove();
       }
      
       <!-- more ejb junk here -->
      }
      


      The deletion of these beans is accomplished through calling PermissionsModifierImpl.deletePermissions(...), which you see in the stack trace above.

      PermissionsModifierImpl.java
       /**
       * Deletes the permissions object associated with this entity.
       * @param entity
       */
       private void deletePermissions(PermissionsCapable entity) throws PermissionsException
       {
       EntityRemover remover = getEntityManagerFactory().getManager().getRemover();
       MutablePermissions permissions = entity.getPermissions();
       if (permissions != null)
       {
       try
       {
       remover.removeEntity(entity.getPermissions());
       entity.setPermissions(null);
       }
       catch (Exception e)
       {
       throw new PermissionsException("Could not delete old permissions for " + entity + ": " + e.getMessage(), e);
       }
       }
       }
      


      Curiously, a failed deletion leaves the child folder bean in a state where its permissions are null, and attempting the modification again will result in success. If the permissions are set on the child folder bean after the failed modification, however, the subsequent modification will also fail.

      We are using similar cascading deletion in other places in our application with no problems. Any ideas as to why this might be happening?