cancel
Showing results for 
Search instead for 
Did you mean: 

Concurrent Modification Exception

rberg
Champ in-the-making
Champ in-the-making
Hello,

I have been getting this error a lot lately, and it leads me to believe that I am not properly using transactions:

09:48:57,825 ERROR [org.alfresco.web.ui.common.Utils] Unable to check out Content Node due to system error:null
java.util.ConcurrentModificationException
   at java.util.HashMap$HashIterator.nextEntry(HashMap.java:787)
   at java.util.HashMap$ValueIterator.next(HashMap.java:817)
   at org.alfresco.repo.search.impl.lucene.LuceneIndexerAndSearcherFactory.prepare(LuceneIndexerAndSearcherFactory.java:684)
   at org.alfresco.repo.transaction.AlfrescoTransactionSupport$TransactionSynchronizationImpl.beforeCommit(AlfrescoTransactionSupport.java:581)
   at org.springframework.transaction.support.AbstractPlatformTransactionManager.triggerBeforeCommit(AbstractPlatformTransactionManager.java:657)
   at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:482)
   at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:469)
   at org.springframework.transaction.interceptor.TransactionAspectSupport.doCommitTransactionAfterReturning(TransactionAspectSupport.java:266)
   at org.alfresco.util.transaction.SpringAwareUserTransaction.commit(SpringAwareUserTransaction.java:376)
   at org.alfresco.web.bean.CheckinCheckoutBean.checkoutFile(CheckinCheckoutBean.java:456)
   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
   at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
   at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
   at java.lang.reflect.Method.invoke(Method.java:585)
   at org.apache.myfaces.el.MethodBindingImpl.invoke(MethodBindingImpl.java:129)
   at org.apache.myfaces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:63)
   at javax.faces.component.UICommand.broadcast(UICommand.java:106)
   at javax.faces.component.UIViewRoot._broadcastForPhase(UIViewRoot.java:90)
   at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:164)
   at org.apache.myfaces.lifecycle.LifecycleImpl.invokeApplication(LifecycleImpl.java:316)
   at org.apache.myfaces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:86)
   at javax.faces.webapp.FacesServlet.service(FacesServlet.java:106)
   at org.alfresco.web.app.servlet.AlfrescoFacesServlet.service(AlfrescoFacesServlet.java:49)
   at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:252)
   at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)
   at org.alfresco.web.app.servlet.AuthenticationFilter.doFilter(AuthenticationFilter.java:73)
   at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:202)
   at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)
   at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:213)
   at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:178)
   at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:126)
   at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:105)
   at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:107)
   at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:148)
   at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:869)
   at org.apache.coyote.http11.Http11BaseProtocol$Http11ConnectionHandler.processConnection(Http11BaseProtocol.java:667)
   at org.apache.tomcat.util.net.PoolTcpEndpoint.processSocket(PoolTcpEndpoint.java:527)
   at org.apache.tomcat.util.net.LeaderFollowerWorkerThread.runIt(LeaderFollowerWorkerThread.java:80)
   at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:684)
   at java.lang.Thread.run(Thread.java:595)


I have an aspect called auditTrail which is bound to the OnContentRead and OnContentUpdate events, and it generates an audit trail entry each time they are called.  But ever since I have implemented this functionality, I have been getting these concurrent modification errors.  The exception above was thrown when I tried to checkout the document. An exception is also thrown if I try to add more than one aspect (one of which is the auditTrail aspect).

Currently my functionality within my onContentRead and onContentUpdate methods are not wrapped in a transaction, could this be the issue. How should I deal with transactions within transactions situations?

Thank you for your help!
-Ryan
1 REPLY 1

rberg
Champ in-the-making
Champ in-the-making
Update:

I was able to avoid this error by implementing TransactionListener. The source is as follows:


public class AuditTrailAspect implements TransactionListener, ContentServicePolicies.OnContentReadPolicy, ContentServicePolicies.OnContentUpdatePolicy
{
   // Members
   private AuthenticationService authenticationService;
    private PolicyComponent policyComponent;
   private AuditTrailService auditTrailService;
    private static final String AUDIT_TRAIL_ITEMS = "AuditTrailAspect.auditTrailItems";

    public void init()
    {
        this.policyComponent.bindClassBehaviour(
                                 ContentServicePolicies.ON_CONTENT_READ,
                                 ContentModel.ASPECT_AUDITTRAIL,
                                 new JavaBehaviour(this, "onContentRead"));

        this.policyComponent.bindClassBehaviour(
                                 ContentServicePolicies.ON_CONTENT_UPDATE,
                                 ContentModel.ASPECT_AUDITTRAIL,
                                 new JavaBehaviour(this, "onContentUpdate"));
    }
   
   public void onContentRead(NodeRef nodeRef)
   {
      AlfrescoTransactionSupport.bindListener(this);

        String currentUser = this.authenticationService.getCurrentUserName();
      Date now = Calendar.getInstance().getTime();
      
      Map<QName, Serializable> auditItemProps = new HashMap<QName, Serializable>();
      auditItemProps.put(ContentModel.PROP_AUDITEVENT, AuditTrailEvents.AUDIT_EVENT_READ);
      auditItemProps.put(ContentModel.PROP_AUDITUSER, currentUser);
      auditItemProps.put(ContentModel.PROP_AUDITTIMESTAMP, now);

        Map<NodeRef, Map<QName, Serializable>> auditItems = getTransactionAuditItems();
        auditItems.put(nodeRef, auditItemProps);
   }

    public void onContentUpdate(NodeRef nodeRef)
    {
        AlfrescoTransactionSupport.bindListener(this);

        String currentUser = this.authenticationService.getCurrentUserName();
      Date now = Calendar.getInstance().getTime();

      Map<QName, Serializable> auditItemProps = new HashMap<QName, Serializable>();
      auditItemProps.put(ContentModel.PROP_AUDITEVENT, AuditTrailEvents.AUDIT_EVENT_UPDATE);
      auditItemProps.put(ContentModel.PROP_AUDITUSER, currentUser);
      auditItemProps.put(ContentModel.PROP_AUDITTIMESTAMP, now);

        Map<NodeRef, Map<QName, Serializable>> auditItems = getTransactionAuditItems();
        auditItems.put(nodeRef, auditItemProps);
    }
   
    /**
     * @param policyComponent  the policy component
     */
    public void setPolicyComponent(PolicyComponent policyComponent)
    {
        this.policyComponent = policyComponent;
    }
   
    /**
     * @param authenticationService  the authentication service
     */
    public void setAuthenticationService(AuthenticationService authenticationService)
    {
        this.authenticationService = authenticationService;
    }
   
   public void setAuditTrailService(AuditTrailService auditTrailService)
   {
      this.auditTrailService = auditTrailService;
   }


    public void flush()
    {
        //NO OP
    }

    public void beforeCommit(boolean readOnly)
    {
        //NO OP
    }

    public void beforeCompletion()
    {
        //NO OP
    }

    public void afterCommit()
    {
        Map<NodeRef, Map<QName, Serializable>> auditItems = getTransactionAuditItems();

        for(NodeRef ref : auditItems.keySet())
        {
            Map<QName, Serializable> props = auditItems.get(ref);
            this.auditTrailService.createAuditTrailEntry(ref, props);
        }

        AlfrescoTransactionSupport.unbindResource(AUDIT_TRAIL_ITEMS);
    }

    public void afterRollback()
    {
        //NO OP
    }

    private Map<NodeRef, Map<QName, Serializable>> getTransactionAuditItems()
    {
        Map<NodeRef, Map<QName, Serializable>> auditItems = (Map<NodeRef, Map<QName, Serializable>>)AlfrescoTransactionSupport.getResource(AUDIT_TRAIL_ITEMS);

        if(auditItems == null)
        {
            auditItems = new HashMap<NodeRef, Map<QName, Serializable>>();
            AlfrescoTransactionSupport.bindResource(AUDIT_TRAIL_ITEMS, auditItems);
        }

        return auditItems;
    }
}

However, now the creation of the audit trail entry is not behaving nicely. It is actually quite strange, there is no error thrown, it just doesn't work.

auditTrailService.createAuditTrailEntry:



public NodeRef createAuditTrailEntry(NodeRef auditedItem, Map<QName, Serializable> auditTrailProps)
   {
      NodeRef auditTrailItem = null;
      
      try
      {

         // get the content id
         String contentId = (String)this.nodeService.getProperty(auditedItem, ContentModel.PROP_NODE_UUID);
         
         // add the content id to the properties
         auditTrailProps.put(ContentModel.PROP_AUDITEDNODEID, contentId);
         
         // create the audit trail item
         auditTrailItem = this.nodeService.createNode(
               getAuditTrailContainer(), ContentModel.ASSOC_CHILDREN,
               ContentModel.TYPE_AUDITTRAILITEM, ContentModel.TYPE_AUDITTRAILITEM,
               auditTrailProps).getChildRef();   
      
      }
      catch(Exception e)
      {
         System.out.println(e);
      }
      
      
      return auditTrailItem;
   }