org.nuiton.topia.replication.TopiaReplicationServiceImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of topia-service-replication Show documentation
Show all versions of topia-service-replication Show documentation
Hibernate based replication service
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 extends TopiaEntity> 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