All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.nuiton.topia.replication.TopiaReplicationServiceImpl Maven / Gradle / Ivy

The newest version!
package org.nuiton.topia.replication;

/*
 * #%L
 * ToPIA :: Service Replication
 * $Id$
 * $HeadURL$
 * %%
 * Copyright (C) 2004 - 2014 CodeLutin
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as 
 * published by the Free Software Foundation, either version 3 of the 
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public 
 * License along with this program.  If not, see
 * .
 * #L%
 */

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuiton.topia.persistence.TopiaApplicationContext;
import org.nuiton.topia.persistence.TopiaEntity;
import org.nuiton.topia.persistence.TopiaEntityEnum;
import org.nuiton.topia.persistence.TopiaException;
import org.nuiton.topia.persistence.TopiaPersistenceContext;
import org.nuiton.topia.persistence.util.TopiaEntityHelper;
import org.nuiton.topia.replication.model.ReplicationModel;
import org.nuiton.topia.replication.model.ReplicationNode;
import org.nuiton.topia.replication.model.ReplicationOperationDef;

import java.util.List;
import java.util.Map;

/**
 * Implantation du service de replication.
 *
 * @author Tony Chemit - [email protected]
 * @since 2.2.0
 */
public class TopiaReplicationServiceImpl implements TopiaReplicationService {

    /** Logger */
    private static final Log log =
            LogFactory.getLog(TopiaReplicationServiceImpl.class);

    /** le contexte sur la base source de la replication */
    protected TopiaApplicationContext context;

    /**
     * le constructeur de modèle de réplication.
     *
     * @since 2.4.3
     */
    protected TopiaReplicationModelBuilder modelBuilder;

    //--------------------------------------------------------------------------
    //-- TopiaService implementation -------------------------------------------
    //--------------------------------------------------------------------------

    @Override
    public void initTopiaService(TopiaApplicationContext topiaApplicationContext, Map serviceConfiguration) {

        if ( ! serviceConfiguration.isEmpty()) {
            if (log.isWarnEnabled()) {
                log.warn("no configuration expected for replication service");
            }
        }

        // set the incoming root context from topia
        this.context = topiaApplicationContext;
        //TODO avoir un objet pour lire les contrainte de resolution de cycle
        //TODO sur les dependances (par exemple, une dependance marquee comme
        // non nulle : ne peut jamais etre dettache, alors que dans le cas
        // contraire on peut dettacher la dependance
        // Cela permet de traiter plus de cas...

        //Properties prop = context.getConfig();
    }

    //--------------------------------------------------------------------------
    //-- TopiaReplicationService implementation --------------------------------
    //--------------------------------------------------------------------------

    @Override
    public ReplicationModel prepare(TopiaEntityEnum[] contracts,
                                    boolean computeOrder,
                                    String... topiaIds) throws TopiaException {
        ReplicationModel model =
                getModelBuilder().prepare(context, contracts, computeOrder, topiaIds);
        return model;
    }

    @Override
    public ReplicationModel prepareForAll(TopiaEntityEnum[] contracts) throws TopiaException {
        ReplicationModel model = getModelBuilder().prepareForAll(contracts);
        return model;
    }


    @Override
    public TopiaReplicationModelBuilder getModelBuilder() {
        if (modelBuilder == null) {
            modelBuilder = new TopiaReplicationModelBuilder();
        }
        return modelBuilder;
    }

    @Override
    public void doReplicate(ReplicationModel model,
                            TopiaApplicationContext targetTx) throws Exception {

        TopiaEntityHelper.checkNotNull("doReplicate", "model", model);
        TopiaEntityHelper.checkNotNull("doReplicate", "dstCtxt", targetTx);

        // create a replication context
        TopiaReplicationContext replicationContext =
                new TopiaReplicationContext(getModelBuilder().getOperationProvider(),
                                            model,
                                            context,
                                            targetTx);

        // init replication context
        replicationContext.init();

        ReplicationNode currentNode = null;
        try {

            for (ReplicationNode node : model.getOrder()) {

                currentNode = node;

                // start replication of current node
                doReplicateNode(replicationContext, node);

                // node was sucessfull replicated, mark it
                replicationContext.addTreatedNode(node);
            }
        } catch (Exception e) {

            if (log.isErrorEnabled()) {
                log.error("Could not replicate node " + currentNode, e);
            }
            // an error occurs, rollback all sucessfull replicated nodes.
            doRollback(replicationContext);

            // then throw the original error
            throw e;
        } finally {
            replicationContext.clear();
        }
    }

    @Override
    public void doRollback(TopiaReplicationContext replicationContext) throws Exception {

        TopiaEntityHelper.checkNotNull("doRollback", "replicationContext", replicationContext);

        ReplicationNode[] treated = replicationContext.getReverseTreated();

        if (treated.length == 0) {

            // aucun noeud de réplication commités
            return;
        }

        log.info("Will rollback " + treated.length + " nodes...");

        for (ReplicationNode node : treated) {

            try {
                doRollbackNode(replicationContext, node);
            } catch (Exception e) {

                log.error("Could not rollback node " + node, e);

                //tchemit 2010-08-17 perharps we should not throw the exception
                // and try to rollback other nodes ?
                throw e;
            }
        }
    }

    public void doReplicateNode(TopiaReplicationContext replicationContext,
                                ReplicationNode node) throws Exception {

        ReplicationOperationDef[] defs = node.getOperations();

        if (defs.length == 0) {
            log.info("skip node " + node + " - no operation detected.");
            return;
        }

        // destination transaction will be opened at the last moment to avoid
        // to open it if something was wrong before...
        TopiaPersistenceContext dstCtxt = null;

        // open transaction on source db (to obtain entities to treate).
        // this transaction must stay open the time of all operations of the
        // node in order to make lazy association loaded by hibernate
        TopiaPersistenceContext srcCtxt = replicationContext.newSourceTx();

        try {
            log.info("start replication for " + node + " : " + defs.length +
                     " operation(s)");

            // get from source db the entities to treate for this node
            List entities =
                    replicationContext.getEntities(srcCtxt, node);

            if (log.isInfoEnabled()) {
                log.info("will replicate on " + entities.size() +
                         " entity(ies)");
            }
            if (log.isDebugEnabled()) {
                for (TopiaEntity entity : entities) {
                    log.debug(entity.getTopiaId());
                }
            }

            // open transaction on target db (the transaction is common to all
            // operations of the node). Each operation must do his own commit
            // if needed
            dstCtxt = replicationContext.newTargetTx();

            for (ReplicationOperationDef def : defs) {

                log.info("start operation " + def);

                TopiaReplicationOperation operation =
                        replicationContext.getOperation(def);

                if (!node.equals(def.getNode())) {

                    // when a node operation is reattached to a later node...
                    entities =
                            replicationContext.getEntities(srcCtxt, def.getNode());
                }

                operation.run(replicationContext,
                              def,
                              srcCtxt,
                              dstCtxt,
                              entities
                );
            }

        } finally {

            try {
                // on rollback le context source (car on a peut-etre modifie
                // des associations ou des dependances mais on ne veut rien
                // retenir au niveau d'hibernate, sinon on s'expose a des erreurs
                // lorsque l'on veut recharger des objets dans le context...)
                TopiaReplicationContext.close(srcCtxt, true);
            } finally {
                if (dstCtxt != null) {
                    // on ferme la transaction sur la base destination
                    // on rollback toujours la transaction, ainsi si une erreur
                    // est survenue la session reste dans un état cohérent.
                    // De plus si les actions n'ont pas fait de commit, la
                    // session reste aussi dans un état incohérent. Le commit
                    // reste toujours à la charge de l'opération 
                    TopiaReplicationContext.close(dstCtxt, true);
                }
            }
        }
    }

    public void doRollbackNode(TopiaReplicationContext replicationContext,
                               ReplicationNode node) throws Exception {

        // recuperation des operations reversibles
        ReplicationOperationDef[] defs = node.getUndoableOperations();

        if (defs.length == 0) {

            // pas d'operation reversible, donc rien a faire sur ce noeud.
            log.info("skip node " + node + " - no reversible operation detected.");
            return;
        }

        log.info("start rollback for " + node + " : " + defs.length +
                 " reversible operation(s)");

        // open transaction on target db
        TopiaPersistenceContext dstCtxt = replicationContext.newTargetTx();

        try {

            for (ReplicationOperationDef def : defs) {

                log.info("start rollback operation " + def);

                TopiaReplicationOperationUndoable operation =
                        replicationContext.getUndoableOperation(def);
                operation.rollback(def, replicationContext, dstCtxt);
            }
        } finally {
            if (dstCtxt != null) {
                // on ferme la transaction sur la base destination
                // on rollback toujours la transaction, ainsi si une erreur
                // est survenue la session reste dans un état cohérent.
                // De plus si les actions n'ont pas fait de commit, la
                // session reste aussi dans un état incohérent. Le commit
                // reste toujours à la charge de l'opération 
                TopiaReplicationContext.close(dstCtxt, true);
            }
        }
    }

    @Override
    public void close() {
        // nothing to do
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy