Alfresco – Implementando el behavior de borrado

El mecanismo de behaviors de Alfresco permite inyectar lógica adicional en el ciclo de vida de los nodos en el repositorio de contenido. Como es habitual, disponéis de un detallado tutorial de Jeff Potts con el que podréis experimentar con este mecanismo.

Sin embargo, en algunos casos, es necesario conocer algunos conceptos adicionales para poder desarrollar un behavior exitoso. A continuación vamos a construir una extensión para la política de borrado de nodos de Alfresco mediante un programa que elimina todas las copias existentes de un documento cuando se borra ese mismo documento.

Implementación básica del behavior

Siguiendo los tutoriales existentes en la Red, podríamos realizar una implementación básica.


public class SimpleBasicDeleteBehaviour implements NodeServicePolicies.BeforeDeleteNodePolicy {

    private PolicyComponent policyComponent;
    private NodeService nodeService;

    public void init() {
        policyComponent.bindClassBehaviour(
    	    NodeServicePolicies.BeforeDeleteNodePolicy.QNAME, 
                ContentModel.TYPE_CONTENT, 
		new JavaBehaviour(this, "beforeDeleteNode", Behaviour.NotificationFrequency.FIRST_EVENT));
    }

    @Override
    public void beforeDeleteNode(NodeRef nodeRef) {
        // Find copied nodes and delete it
        for (AssociationRef copyAssoc : nodeService.getSourceAssocs(nodeRef,ContentModel.ASSOC_ORIGINAL)) {
            nodeService.deleteNode(copyAssoc.getSourceRef());
        }
    }
}

En caso de que utilicemos este código en nuestra implantación de Alfresco observaremos que aquellos nodos que tengan copias no van a poder ser eliminados. A pesar de que no aparecerá ningún error en la traza de ejecución.

Conceptos adicionales

  • Cuando una transacción de Alfresco implica la eliminación de un nodo, no se permitirá realizar ninguna operación de modificación o borrado en las políticas
  • El behavior se ejecuta integrado en la misma transacción de borrado del nodo que está asociada al thread de ejecución

Implementación transaccional del behavior

En consecuencia de lo anterior, vamos a modificar nuestro desarrollo para ejecutar de manera desacoplada (tanto a nivel transaccional como a nivel de thread) el borrado de los nodos que son copia del original.

public class TransactionalDeleteBehaviour 
    implements NodeServicePolicies.BeforeDeleteNodePolicy {
    
    // Key to identify resources associated to transaction
    private static final String KEY_RELATED_NODES = 
            TransactionalDeleteBehaviour.class.getName() + ".relatedNodes";
    
    private PolicyComponent policyComponent;
    private NodeService nodeService;
    private TransactionService transactionService;
    private TransactionListener transactionListener;
    private ThreadPoolExecutor threadPoolExecutor;
    
    // Bind behaviour and initialize transaction listener
    public void init() {
        
        policyComponent.bindClassBehaviour(
                NodeServicePolicies.BeforeDeleteNodePolicy.QNAME, 
                ContentModel.TYPE_CONTENT, 
                new JavaBehaviour(this, "beforeDeleteNode", Behaviour.NotificationFrequency.FIRST_EVENT));
        
        this.transactionListener = new RelatedNodesTransactionListener();
        
    }
    

    @Override
    public void beforeDeleteNode(NodeRef nodeRef) {
        
        // Bind listener to current transaction
        AlfrescoTransactionSupport.bindListener(transactionListener);

        // Get some related nodes to work with
        List<NodeRef> relatedNodes = new ArrayList<NodeRef>();
        for (AssociationRef copyAssoc : nodeService.getSourceAssocs(nodeRef, ContentModel.ASSOC_ORIGINAL)) {
            relatedNodes.add(copyAssoc.getSourceRef());
        }

        // Transactions involving several nodes need resource updating
        List<NodeRef> currentRelatedNodes = AlfrescoTransactionSupport.getResource(KEY_RELATED_NODES);
        if (currentRelatedNodes == null) {
            currentRelatedNodes = relatedNodes;
        } else {
            currentRelatedNodes.addAll(relatedNodes);
        }
        
        // Put resources to be used in transaction listener
        AlfrescoTransactionSupport.bindResource(KEY_RELATED_NODES, currentRelatedNodes);

    }
    
    // Listening "afterCommit" transaction event
    private class RelatedNodesTransactionListener 
        extends TransactionListenerAdapter implements TransactionListener {

        @Override
        public void afterCommit() {
            @SuppressWarnings("unchecked")
            List<NodeRef> nodesToBeReviewed = 
                (List<NodeRef>) AlfrescoTransactionSupport.getResource(KEY_RELATED_NODES);
            if (nodesToBeReviewed != null) {
                for (NodeRef nodeToBeReviewed : nodesToBeReviewed) {
                    // Launch every node work in a different thread
                    Runnable runnable = new RelatedNodeDeletion(nodeToBeReviewed);
                    threadPoolExecutor.execute(runnable);
                }
            }
        }
        
        @Override
        public void flush() {
        }
        
    }
    
    // Thread to work with an individual node
    private class RelatedNodeDeletion implements Runnable {
        
        private NodeRef nodeToBeReviewed;
        
        private RelatedNodeDeletion(NodeRef nodeToBeReviewed) {
            this.nodeToBeReviewed = nodeToBeReviewed;
        }

        @Override
        public void run() {
            AuthenticationUtil.runAsSystem(new RunAsWork<Void>() {
                
                public Void doWork() throws Exception {
                    
                    RetryingTransactionCallback<Void> callback = new RetryingTransactionCallback<Void>() {
                        
                        @Override
                        public Void execute() throws Throwable {
                            nodeService.deleteNode(nodeToBeReviewed);
                            return null;
                        }
                    };
                    
                    try {
                        RetryingTransactionHelper txnHelper = 
                            transactionService.getRetryingTransactionHelper();
                        txnHelper.doInTransaction(callback, false, true);
                    } catch (Throwable e) {
                        e.printStackTrace();
                    }
                    
                    return null;
                    
                }
            });
        }
        
    }

De esta manera, la eliminación de nodos copiados se realizará en modo asíncrono una vez que haya terminado la eliminación del nodo original y evitaremos los conflictos con el control transaccional por defecto de Alfresco.

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s