Alexandre Hausherr
Alexandre Hausherr's Blog

Alexandre Hausherr's Blog

How to execute code at the very end of a transaction in Alfresco

Alexandre Hausherr's photo
Alexandre Hausherr
·Apr 26, 2022·

2 min read

Sometimes we mix actions and behaviors in Alfresco to automate tasks, and it is hard to have control over their execution order. It is especially true when you mix behaviors and action.
Depending on the frequency of the behavior, a behavior might trigger the action which might trigger the behavior again. So even with a TRANSACTION_COMMIT frequency, your code is not guaranteed to execute at the very end of the transaction.
And in some cases it can cause unexpected bugs and the whole transaction to fail .

I met this case once when I wrote an action which performed several operations on a node on update, and at the end it had to restrict the permissions for the current user on the node depending on the value of some property.

The action was working, but in my case, another action was triggered on the node right after, which always leaded to thrown exceptions (and failure of the transaction) since the current user didn't have the read permission on the node anymore.

So I found a solution to execute the permissions restriction from the first action at the very end of the transaction, after all actions and behaviors executions.

Here is a snippet of the solution :

// Some service with a 'doSomething' method which is supposed to be executed by an action on update, or a behavior. 
public class SomeService {

    private TransactionListener transactionListener;

    public SomeService() {
        this.transactionListener = new PermissionsModifierTransactionListener();
    }


    public void doSomeThing(NodeRef node) {
        // Do some stuff ... 
        // Then plan the permissions restriction at the end of the transaction :

        // Bind listener to current transaction
        AlfrescoTransactionSupport.bindListener(transactionListener);

        // Get some related nodes to work with
        List<NodeRef> nodes = new ArrayList<NodeRef>();
        nodes.add(node);

        // Transactions involving several nodes need resource updating
        List<NodeRef> existingNodes = AlfrescoTransactionSupport.getResource(KEY_TX_NODES);
        if (existingNodes == null) {
            existingNodes = nodes;
        } else {
            existingNodes.addAll(nodes);
        }

        // Put resources to be used in transaction listener
        AlfrescoTransactionSupport.bindResource(KEY_TX_NODES, existingNodes);
    }    


    // Transaction listener which changes perms of a list of nodes at the end of a transaction, just before commit .
    private class PermissionsModifierTransactionListener extends TransactionListenerAdapter implements TransactionListener {

        @Override
        public void flush() {}

        @Override
        public void beforeCommit(boolean readOnly) {
            List<NodeRef> nodes = AlfrescoTransactionSupport.getResource(KEY_TX_NODES);
            AuthenticationUtil.runAs(() -> {
                // Remove permissions inheritance on the bound nodes.
                nodes.forEach(node -> permissionService.setInheritParentPermissions(node, false);
                return null;
            }, AuthenticationUtil.getAdminUserName());
        }
    }
}

The solution was found in this blog post :

angelborroy.wordpress.com/2015/05/22/alfres..

 
Share this