Author: tchemit Date: 2010-08-21 18:05:12 +0200 (Sat, 21 Aug 2010) New Revision: 2105 Url: http://nuiton.org/repositories/revision/topia/2105 Log: - Anomalie #779: Replication is not atomic - Evolution #815: Rethink replication service Added: trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationContext.java trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationModelBuilder.java trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationOperationProvider.java trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationOperationUndoable.java Removed: trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationModelBuilder.java trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationModelBuilderImpl.java trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationOperationProvider.java trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationOperationProviderImpl.java Modified: trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationOperation.java trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationService.java trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationServiceImpl.java trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/model/ReplicationLink.java trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/model/ReplicationModel.java trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/model/ReplicationNode.java trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/model/ReplicationOperationDef.java trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/operation/AttachAssociation.java trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/operation/AttachLink.java trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/operation/DettachAssociation.java trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/operation/Duplicate.java trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/operation/LoadLink.java trunk/topia-service-replication/src/main/resources/i18n/topia-service-replication-en_GB.properties trunk/topia-service-replication/src/main/resources/i18n/topia-service-replication-fr_FR.properties trunk/topia-service-replication/src/test/java/org/nuiton/topia/replication/AbstractTopiaReplicationServiceTest.java trunk/topia-service-replication/src/test/java/org/nuiton/topia/replication/TopiaReplicationOperationTest.java trunk/topia-service-replication/src/test/java/org/nuiton/topia/replication/TopiaReplicationServiceImplAllTest.java trunk/topia-service-replication/src/test/java/org/nuiton/topia/replication/TopiaReplicationServiceImplTest.java trunk/topia-service-replication/src/test/java/org/nuiton/topia/replication/operation/FakeOperation.java trunk/topia-service-replication/src/test/java/org/nuiton/topia/replication/operation/UncreatableOperation.java trunk/topia-service-replication/src/test/java/org/nuiton/topia/replication/operation/UnregistredOperation.java Added: trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationContext.java =================================================================== --- trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationContext.java (rev 0) +++ trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationContext.java 2010-08-21 16:05:12 UTC (rev 2105) @@ -0,0 +1,247 @@ +package org.nuiton.topia.replication; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.framework.TopiaContextImplementor; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.TopiaEntityEnum; +import org.nuiton.topia.persistence.util.TopiaEntityHelper; +import org.nuiton.topia.persistence.util.TopiaEntityIdsMap; +import org.nuiton.topia.replication.model.ReplicationModel; +import org.nuiton.topia.replication.model.ReplicationNode; +import org.nuiton.topia.replication.model.ReplicationOperationDef; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Defines the context used to replicate. + * <p/> + * It contains : + * <ul> + * <li>the {@link #replicationModel}</li> + * <li>data sources involved ([@link #sourceTx}, {@link #targetTx})</li> + * <li>universe of data to replicate {@link #data}</li> + * <li>nodes that has been treated successfully {@link #treated}</li> + * </ul> + * + * @author tchemit <chemit@codelutin.com> + * @since 2.4.3 + */ +public class TopiaReplicationContext { + + /** Logger */ + private static final Log log = + LogFactory.getLog(TopiaReplicationContext.class); + + /** Replication model. */ + protected final ReplicationModel replicationModel; + + /** + * root context of incoming data source (says where to obtain entities + * to replicate). + */ + protected final TopiaContext sourceTx; + + /** + * root context of outcmoing data source (says where to replicate + * entities). + */ + protected final TopiaContext targetTx; + + /** dictionnary of data to replicate. */ + protected TopiaEntityIdsMap data; + + /** + * List of nodes that has been replicated with success (used to + * rollback them if something was wrong). + */ + protected List<ReplicationNode> treated; + + protected TopiaReplicationOperationProvider operationProvider; + + public TopiaReplicationContext(TopiaReplicationOperationProvider operationProvider, + ReplicationModel replicationModel, + TopiaContext sourceTx, + TopiaContext targetTx) { + this.operationProvider = operationProvider; + this.replicationModel = replicationModel; + this.sourceTx = sourceTx; + this.targetTx = targetTx; + } + + public TopiaReplicationOperation getOperation(ReplicationOperationDef operationDef) { + TopiaReplicationOperation operation = + operationProvider.getOperation(operationDef); + return operation; + } + + public TopiaReplicationOperationUndoable getUndoableOperation(ReplicationOperationDef operationDef) throws IllegalArgumentException { + TopiaReplicationOperationUndoable operation = + operationProvider.getUndoableOperation(operationDef); + return operation; + } + + public ReplicationModel getReplicationModel() { + return replicationModel; + } + + public void addTreatedNode(ReplicationNode node) { + getTreated().add(node); + } + + public ReplicationNode[] getReverseTreated() { + + List<ReplicationNode> result = + new ArrayList<ReplicationNode>(getTreated()); + + // reverse nodes treated + Collections.reverse(result); + + return result.toArray(new ReplicationNode[result.size()]); + } + + + /** + * Init the replication context. + * <p/> + * Comptute the universe to replicate and store it in {@link #data}. + * + * @throws TopiaException for any error when initializing the replication context + */ + public void init() throws TopiaException { + + TopiaContextImplementor srcCtxt = newSourceTx(); + try { + + // obtain all data to replicate + data = getIds(srcCtxt); + + // adjust operation according to this data (some operations can be + // skipped if no data is associated to the given nodes) + // will also sort for each node his operations (according to + // their respective phase) + getReplicationModel().adjustOperations(data); + + } finally { + close(srcCtxt, true); + } + } + + public List<String> getEntityIds(Class<? extends TopiaEntity> type) throws TopiaException { + + List<String> nodeEntityIds = data.get(type); + return nodeEntityIds; + } + + public List<String> getEntityIds(ReplicationNode node) throws TopiaException { + + List<String> nodeEntityIds = getEntityIds(node.getEntityType()); + return nodeEntityIds; + } + + public List<? extends TopiaEntity> getEntities(TopiaContextImplementor tx, + ReplicationNode node) throws TopiaException { + + List<String> nodeEntityIds = getEntityIds(node); + + if (log.isDebugEnabled()) { + log.debug("Will load " + nodeEntityIds.size() + " entities"); + } + + List<? extends TopiaEntity> nodeEntities = + TopiaEntityHelper.getEntitiesList( + tx, + nodeEntityIds.toArray(new String[nodeEntityIds.size()]) + ); + return nodeEntities; + + } + + public TopiaContextImplementor newSourceTx() throws TopiaException { + return (TopiaContextImplementor) sourceTx.beginTransaction(); + } + + public TopiaContextImplementor newTargetTx() throws TopiaException { + return (TopiaContextImplementor) targetTx.beginTransaction(); + } + + public static void close(TopiaContext tx, boolean rollback) throws TopiaException { + + try { + if (rollback) { + tx.rollbackTransaction(); + } + } finally { + tx.closeContext(); + } + } + + public void clear() { + if (data != null) { + data.clear(); + } + if (treated != null) { + treated.clear(); + } + } + + protected TopiaEntityIdsMap getIds(TopiaContextImplementor srcCtxt) throws TopiaException { + + TopiaEntityIdsMap data; + + // on recupere les objets a repliquer par type + if (replicationModel.isReplicateAll()) { + + // on recupere pour chaque type tous les ids des entites a repliquer + data = new TopiaEntityIdsMap(); + for (TopiaEntityEnum e : replicationModel.getContracts()) { + List<String> ids = srcCtxt.getDAO(e.getContract()).findAllIds(); + data.put(e.getContract(), ids); + } + } else { + + // on recupere les entites specifies a repliquer + TopiaEntity[] entities = TopiaEntityHelper.getEntities( + srcCtxt, + replicationModel.getTopiaIds() + ); + + // on calcule toutes les ids des entites a repliquer + data = TopiaEntityHelper.detectEntityIds( + replicationModel.getContracts(), + replicationModel.getTypes(), + entities + ); + } + return data; + } + + protected List<ReplicationNode> getTreated() { + if (treated == null) { + treated = new ArrayList<ReplicationNode>(); + } + return treated; + } + + protected TopiaContext getSourceTx() { + return sourceTx; + } + + protected TopiaContext getTargetTx() { + return targetTx; + } + + protected TopiaEntityIdsMap getData() { + return data; + } + + @Override + protected void finalize() throws Throwable { + super.finalize(); + clear(); + } +} Deleted: trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationModelBuilder.java =================================================================== --- trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationModelBuilder.java 2010-08-21 16:02:52 UTC (rev 2104) +++ trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationModelBuilder.java 2010-08-21 16:05:12 UTC (rev 2105) @@ -1,146 +0,0 @@ -/* - * #%L - * ToPIA :: Service Replication - * - * $Id$ - * $HeadURL$ - * %% - * Copyright (C) 2004 - 2010 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 - * <http://www.gnu.org/licenses/lgpl-3.0.html>. - * #L% - */ -package org.nuiton.topia.replication; - -import org.nuiton.topia.TopiaContext; -import org.nuiton.topia.TopiaException; -import org.nuiton.topia.persistence.TopiaEntity; -import org.nuiton.topia.persistence.TopiaEntityEnum; -import org.nuiton.topia.replication.model.ReplicationModel; -import org.nuiton.topia.replication.model.ReplicationOperationPhase; - -import java.util.Set; - -/** - * Helper to create {@link ReplicationModel}. - * - * @author tchemit <chemit@codelutin.com> - * @since 2.4.3 - */ -public interface TopiaReplicationModelBuilder { - - TopiaReplicationOperationProvider getOperationProvider(); - - TopiaReplicationOperation getOperation(Class<? extends TopiaReplicationOperation> operationClass); - - TopiaReplicationOperation[] getOperations(); - - /** - * Creation d'un modele de replication non initialise. - * - * @param context topia context on which entities are loaded - * @param contracts le contrats d'entites autorises - * @param topiaIds les ids des entites a repliquer - * @return le modele instancie mais non initialise - * @throws TopiaException pour toute erreur - */ - ReplicationModel createModel(TopiaContext context, - TopiaEntityEnum[] contracts, - String... topiaIds) throws TopiaException; - - /** - * Creation d'un modele de replication non initialise avec un ordre fixe - * (celui des contrats donnés). - * - * @param contracts le contrats d'entites autorises - * @param topiaIds les ids des entities a repliquer - * @return le modele instancie mais non initialise - * @throws TopiaException pour toute erreur - */ - ReplicationModel createModelWithComputedOrder( - TopiaEntityEnum[] contracts, - String... topiaIds) throws TopiaException; - - /** - * Creation d'un modele de replication (en mode tout dupliquer) non initialise. - * - * @param contracts le contrats d'entites autorises - * @return le modele instancie mais non initialise - * @throws TopiaException pour toute erreur - */ - ReplicationModel createModelForAll(TopiaEntityEnum[] contracts) - throws TopiaException; - - - ReplicationModel prepare(TopiaContext context, - TopiaEntityEnum[] contracts, - String... entities) throws TopiaException; - - /** - * Prepare le modele de replication pour toutes les entites des types - * donnes. - * <p/> - * La méthode calcule l'ordre de replication des données. - * <p/> - * Actuellement, on n'est pas capable de calculer l'ordre si le graphe des - * entités contient des cycles. - * <p/> - * TODO : faire en sorte de pouvoir gérer les cycles. - * - * @param contracts les contrats des types a repliquer - * @return le modele pour la replication - * @throws TopiaException pour toute erreur rencontree - */ - ReplicationModel prepareForAll(TopiaEntityEnum[] contracts) throws TopiaException; - - /** - * Prepare le modele de replication pour les entites données en ne faisant - * pas de calcul sur l'ordre des entités à répliquer. - * <p/> - * L'ordre des types donnees sera celui utilisé. - * - * @param contracts les contrats a repliquer (dans l'ordre donnée) - * @param topiaIds les ids des entites a repliquer - * @return le model de replication initialise - * @throws TopiaException pour toute erreur recontree - */ - ReplicationModel prepareWithComputedOrder(TopiaEntityEnum[] contracts, - String... topiaIds) throws TopiaException; - - Set<Class<? extends TopiaEntity>> detectTypes( - TopiaContext context, - TopiaEntityEnum[] contracts, String... ids) throws TopiaException; - - ReplicationModel initModel(ReplicationModel model, - boolean computeOrder) - throws TopiaException; - - void addBeforeOperation(ReplicationModel model, - TopiaEntityEnum type, - Class<? extends TopiaReplicationOperation> operationClass, - Object... parameters); - - void addAfterOperation(ReplicationModel model, - TopiaEntityEnum type, - Class<? extends TopiaReplicationOperation> operationClass, - Object... parameters); - - void createOperation( - ReplicationModel model, - TopiaEntityEnum type, - ReplicationOperationPhase phase, - Class<? extends TopiaReplicationOperation> operationClass, - Object... parameters); -} Copied: trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationModelBuilder.java (from rev 2101, trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationModelBuilderImpl.java) =================================================================== --- trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationModelBuilder.java (rev 0) +++ trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationModelBuilder.java 2010-08-21 16:05:12 UTC (rev 2105) @@ -0,0 +1,304 @@ +/* + * #%L + * ToPIA :: Service Replication + * + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2010 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 + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topia.replication; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.TopiaEntityEnum; +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.ReplicationOperationPhase; + +import java.util.Arrays; +import java.util.Set; + +import static org.nuiton.i18n.I18n._; + +/** + * Builder of {@link ReplicationModel}. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.4.3 + */ +public class TopiaReplicationModelBuilder { + + /** Logger */ + private static final Log log = + LogFactory.getLog(TopiaReplicationModelBuilder.class); + + /** Provider of {@link TopiaReplicationOperation}. */ + protected TopiaReplicationOperationProvider operationProvider; + + public TopiaReplicationOperationProvider getOperationProvider() { + if (operationProvider == null) { + operationProvider = new TopiaReplicationOperationProvider(); + } + return operationProvider; + } + + /** + * Prepare le modele de replication pour les entites données dans + * {@code topiaIds} et de leur couverture. + * <p/> + * Le paramètre {@code computeOrder} détermine si on doit calculer l'ordre + * de replication des données (valeur à {@code true}), sinon (valeur + * à {@code false}) on utilise l'ordre induit par le paramètre + * {@code contracts}. + * + * @param context le context de la base source (peut être utilisé + * pour calculer l'ordre) + * @param contracts les contrats des types a repliquer + * @param computeOrder drapeau positionné à {@code true} si on doit calculer + * l'ordre de réplication, {@code false} si on utilise + * l'ordre induit par le paramètre {@code contracts}. + * @param topiaIds les ids des entités à répliquer + * @return le modele pour la replication + * @throws TopiaException pour toute erreur rencontree + */ + public ReplicationModel prepare(TopiaContext context, + TopiaEntityEnum[] contracts, + boolean computeOrder, + String... topiaIds) throws TopiaException { + ReplicationModel model = + createModel(context, contracts, computeOrder, topiaIds); + initModel(model, computeOrder); + return model; + } + + /** + * Prepare le modele de replication pour toutes les entites des types + * donnes. + * <p/> + * La méthode calcule l'ordre de replication des données. + * <p/> + * Actuellement, on n'est pas capable de calculer l'ordre si le graphe des + * entités contient des cycles. + * <p/> + * TODO : faire en sorte de pouvoir gérer les cycles. + * + * @param contracts les contrats des types a repliquer + * @return le modele pour la replication + * @throws TopiaException pour toute erreur rencontree + */ + public ReplicationModel prepareForAll(TopiaEntityEnum[] contracts) throws TopiaException { + ReplicationModel model = createModelForAll(contracts); + initModel(model, true); + return model; + } + + public void createOperation(ReplicationModel model, + TopiaEntityEnum type, + ReplicationOperationPhase phase, + Class<? extends TopiaReplicationOperation> operationClass, + Object... parameters) { + + TopiaEntityHelper.checkNotNull("createOperation", "model", model); + TopiaEntityHelper.checkNotNull("createOperation", "type", type); + TopiaEntityHelper.checkNotNull("createOperation", "phase", phase); + TopiaEntityHelper.checkNotNull("createOperation", "operationClass", operationClass); + + TopiaReplicationOperation operation = + getOperationProvider().getOperation(operationClass); + + if (operation == null) { + throw new IllegalArgumentException( + _("topia.replication.error.unkown.operation", + operationClass.getSimpleName(), + Arrays.toString(getOperationProvider().getOperations())) + ); + } + + ReplicationNode node = model.getNode(type); + if (node == null) { + throw new IllegalArgumentException( + _("topia.replication.error.unkown.owner.node", + type, + operationClass.getSimpleName(), + model.getNodes()) + ); + } + operation.register(model, node, phase, parameters); + } + + /** + * Ajouter une nouvelle operation pre-replication, sur un type de donnee. + * + * @param model le modele de replication + * @param type le type du noeud de replication + * @param operationClass l'implantation de l'operation + * @param parameters les parametres supplementaires pour l'operation + */ + public void addBeforeOperation(ReplicationModel model, + TopiaEntityEnum type, + Class<? extends TopiaReplicationOperation> operationClass, + Object... parameters) { + createOperation( + model, + type, + ReplicationOperationPhase.before, + operationClass, + parameters + ); + } + + /** + * Ajouter une nouvelle operation post-replication, sur un type de donnee. + * + * @param model le modele de replication + * @param type le type du noeud de replication + * @param operationClass l'implantation de l'operation + * @param parameters les parametres supplementaires pour l'operation + */ + public void addAfterOperation(ReplicationModel model, + TopiaEntityEnum type, + Class<? extends TopiaReplicationOperation> operationClass, + Object... parameters) { + createOperation( + model, + type, + ReplicationOperationPhase.after, + operationClass, + parameters + ); + } + + /** + * Instantie un nouveau modèle de réplication pour les entités données par + * leur ids. + * <p/> + * L'ordre de réplication dépend du paramètre {@code computeOrder}. Si + * celui-ci vaut {@code true}, on calcule l'ordre de réplication, sinon on + * utilise l'ordre induit par les {@code contracts}. + * + * @param context le context Topia pour récupérer certainnes + * informations de la base source si nécessaire. + * @param contracts les types d'entités + * @param computeOrder drapeau pour calculer l'ordre de réplication + * (valeur à {@code true}), sinon on utilise l'ordre + * induit par les {@code contracts}. + * @param topiaIds les ids à dupliquer + * @return le modèle crée mais non initialisé. + * @throws TopiaException pour toute erreur lors de la création du modèle + */ + public ReplicationModel createModel(TopiaContext context, + TopiaEntityEnum[] contracts, + boolean computeOrder, + String... topiaIds) + throws TopiaException { + + ReplicationModel model; + + if (computeOrder) { + // determines types to use + Set<Class<? extends TopiaEntity>> detectTypes = + detectTypes(context, contracts, topiaIds); + model = new ReplicationModel(contracts, detectTypes, topiaIds); + } else { + model = new ReplicationModel(contracts, false, topiaIds); + + } + return model; + } + + /** + * Instantie un nouveau modèle de réplication en respectant l'ordre induit + * par les {@code contracts}. + * + * @param contracts les types d'entités + * @param topiaIds les ids à dupliquer + * @return le modèle crée mais non initialisé. + * @throws TopiaException pour toute erreur lors de la création du modèle + * @deprecated since 2.4.3, prefer use method {@link #createModel(TopiaContext, TopiaEntityEnum[], boolean, String...)} + */ + @Deprecated + public ReplicationModel createModelWithComputedOrder(TopiaEntityEnum[] contracts, + String... topiaIds) + throws TopiaException { + + ReplicationModel model; + + + model = new ReplicationModel(contracts, false, topiaIds); + return model; + } + + /** + * Instantie un nouveau modèle de réplication pour toutes les entitées. + * <p/> + * Ici, l'ordre est toujours calculé. + * + * @param contracts les types d'entités + * @return le modèle crée mais non initialisé. + * @throws TopiaException pour toute erreur lors de la création du modèle + */ + public ReplicationModel createModelForAll(TopiaEntityEnum[] contracts) + throws TopiaException { + ReplicationModel model = new ReplicationModel(contracts, true); + return model; + } + + public ReplicationModel initModel(ReplicationModel model, + boolean computeOrder) throws TopiaException { + TopiaEntityHelper.checkNotNull("initModel", "model", model); + + model.detectAssociations(); + model.detectDirectDependencies(); + if (computeOrder) { + model.detectShell(); + model.detectDependencies(); + } + model.detectObjectsToDettach(); + model.detectOperations(); + return model; + } + + protected Set<Class<? extends TopiaEntity>> detectTypes(TopiaContext context, + TopiaEntityEnum[] contracts, + String... ids) throws TopiaException { + + TopiaContext ctxt = context.beginTransaction(); + try { + TopiaEntity[] entities = TopiaEntityHelper.getEntities(ctxt, ids); + + // on detecte tous les types connus pour les entites données + Set<Class<? extends TopiaEntity>> types = + TopiaEntityHelper.detectTypes(contracts, entities); + + if (log.isDebugEnabled()) { + log.debug("for type : " + entities.getClass()); + for (Class<? extends TopiaEntity> k : types) { + log.debug(k); + } + } + return types; + } finally { + ctxt.closeContext(); + } + } +} Deleted: trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationModelBuilderImpl.java =================================================================== --- trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationModelBuilderImpl.java 2010-08-21 16:02:52 UTC (rev 2104) +++ trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationModelBuilderImpl.java 2010-08-21 16:05:12 UTC (rev 2105) @@ -1,258 +0,0 @@ -/* - * #%L - * ToPIA :: Service Replication - * - * $Id$ - * $HeadURL$ - * %% - * Copyright (C) 2004 - 2010 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 - * <http://www.gnu.org/licenses/lgpl-3.0.html>. - * #L% - */ -package org.nuiton.topia.replication; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.nuiton.topia.TopiaContext; -import org.nuiton.topia.TopiaException; -import org.nuiton.topia.persistence.TopiaEntity; -import org.nuiton.topia.persistence.TopiaEntityEnum; -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.ReplicationOperationPhase; - -import java.util.Arrays; -import java.util.Set; - -import static org.nuiton.i18n.I18n._; - -/** - * Default implementation of {@link TopiaReplicationModelBuilder}. - * - * @author tchemit <chemit@codelutin.com> - * @since 2.4.3 - */ -public class TopiaReplicationModelBuilderImpl implements TopiaReplicationModelBuilder { - - /** Logger */ - private static final Log log = - LogFactory.getLog(TopiaReplicationModelBuilderImpl.class); - - protected TopiaReplicationOperationProvider operationProvider; - - @Override - public TopiaReplicationOperationProvider getOperationProvider() { - if (operationProvider == null) { - operationProvider = new TopiaReplicationOperationProviderImpl(); - } - return operationProvider; - } - - @Override - public TopiaReplicationOperation getOperation(Class<? extends TopiaReplicationOperation> operationClass) { - return getOperationProvider().getOperation(operationClass); - } - - @Override - public TopiaReplicationOperation[] getOperations() { - return getOperationProvider().getOperations(); - } - - - @Override - public ReplicationModel createModel(TopiaContext context, - TopiaEntityEnum[] contracts, - String... topiaIds) - throws TopiaException { - - Set<Class<? extends TopiaEntity>> detectTypes = - detectTypes(context, contracts, topiaIds); - ReplicationModel model = - new ReplicationModel(contracts, detectTypes, topiaIds); - return model; - } - - - @Override - public ReplicationModel createModelWithComputedOrder( - TopiaEntityEnum[] contracts, - String... topiaIds) - throws TopiaException { - ReplicationModel model = - new ReplicationModel(contracts, false, topiaIds); - return model; - } - - - @Override - public ReplicationModel createModelForAll(TopiaEntityEnum[] contracts) - throws TopiaException { - ReplicationModel model = new ReplicationModel(contracts, true); - return model; - } - - @Override - public void createOperation( - ReplicationModel model, - TopiaEntityEnum type, - ReplicationOperationPhase phase, - Class<? extends TopiaReplicationOperation> operationClass, - Object... parameters) { - - TopiaEntityHelper.checkNotNull("createOperation", "provider", operationProvider); - TopiaEntityHelper.checkNotNull("createOperation", "model", model); - TopiaEntityHelper.checkNotNull("createOperation", "type", type); - TopiaEntityHelper.checkNotNull("createOperation", "phase", phase); - TopiaEntityHelper.checkNotNull("createOperation", "operationClass", operationClass); - - TopiaReplicationOperation operation = operationProvider.getOperation(operationClass); - - if (operation == null) { - throw new IllegalArgumentException( - _("topia.replication.engine.error.unkown.operation", - operationClass.getSimpleName(), - Arrays.toString(operationProvider.getOperations())) - ); - } - - ReplicationNode node = model.getNode(type); - if (node == null) { - throw new IllegalArgumentException( - _("topia.replication.engine.error.unkown.owner.node", - type, - operationClass.getSimpleName(), - model.getNodes()) - ); - } - operation.register(model, node, phase, parameters); - } - - - @Override - public ReplicationModel prepare(TopiaContext context, - TopiaEntityEnum[] contracts, - String... entities) throws TopiaException { - ReplicationModel model = - createModel(context, contracts, entities); - initModel(model, true); - return model; - } - - - @Override - public ReplicationModel prepareForAll(TopiaEntityEnum[] contracts) throws TopiaException { - ReplicationModel model = createModelForAll(contracts); - initModel(model, true); - return model; - } - - - @Override - public ReplicationModel prepareWithComputedOrder(TopiaEntityEnum[] contracts, - String... topiaIds) throws TopiaException { - ReplicationModel model = - createModelWithComputedOrder(contracts, topiaIds); - initModel(model, false); - return model; - } - - @Override - public Set<Class<? extends TopiaEntity>> detectTypes(TopiaContext context, - TopiaEntityEnum[] contracts, - String... ids) throws TopiaException { - - TopiaContext ctxt = context.beginTransaction(); - try { - TopiaEntity[] entities = TopiaEntityHelper.getEntities(ctxt, ids); - - // on detecte tous les types connus pour les entites données - Set<Class<? extends TopiaEntity>> types = - TopiaEntityHelper.detectTypes(contracts, entities); - - if (log.isDebugEnabled()) { - log.debug("for type : " + entities.getClass()); - for (Class<? extends TopiaEntity> k : types) { - log.debug(k); - } - } - return types; - } finally { - ctxt.closeContext(); - } - } - - @Override - public ReplicationModel initModel(ReplicationModel model, - boolean computeOrder) throws TopiaException { - TopiaEntityHelper.checkNotNull("initModel", "model", model); - - model.detectAssociations(); - model.detectDirectDependencies(); - if (computeOrder) { - model.detectShell(); - model.detectDependencies(); - } - model.detectObjectsToDettach(); - model.detectOperations(); - return model; - } - - - /** - * Ajouter une nouvelle operation pre-replication, sur un type de donnee. - * - * @param model le modele de replication - * @param type le type du noeud de replication - * @param operationClass l'implantation de l'operation - * @param parameters les parametres supplementaires pour l'operation - */ - @Override - public void addBeforeOperation(ReplicationModel model, - TopiaEntityEnum type, - Class<? extends TopiaReplicationOperation> operationClass, - Object... parameters) { - createOperation( - model, - type, - ReplicationOperationPhase.before, - operationClass, - parameters - ); - } - - /** - * Ajouter une nouvelle operation post-replication, sur un type de donnee. - * - * @param model le modele de replication - * @param type le type du noeud de replication - * @param operationClass l'implantation de l'operation - * @param parameters les parametres supplementaires pour l'operation - */ - @Override - public void addAfterOperation(ReplicationModel model, - TopiaEntityEnum type, - Class<? extends TopiaReplicationOperation> operationClass, - Object... parameters) { - createOperation( - model, - type, - ReplicationOperationPhase.after, - operationClass, - parameters - ); - } -} Modified: trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationOperation.java =================================================================== --- trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationOperation.java 2010-08-21 16:02:52 UTC (rev 2104) +++ trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationOperation.java 2010-08-21 16:05:12 UTC (rev 2105) @@ -28,7 +28,6 @@ import org.nuiton.topia.framework.TopiaContextImplementor; import org.nuiton.topia.persistence.TopiaEntity; import org.nuiton.topia.persistence.TopiaEntityEnum; -import org.nuiton.topia.persistence.util.TopiaEntityIdsMap; import org.nuiton.topia.replication.model.ReplicationModel; import org.nuiton.topia.replication.model.ReplicationNode; import org.nuiton.topia.replication.model.ReplicationOperationDef; @@ -41,13 +40,13 @@ * Le contrat d'une operation a effectuer lors de la replication. * <p/> * Le module propose des operations de base dans le paquetage - * <code>org.nuiton.topia.replication.operation</code>. + * {@code org.nuiton.topia.replication.operation}. * <p/> * <p/> * Pour definir une nouvelle implantation d'operation, il faut l'enregister * en tant que service (au sens de la classe {@link ServiceLoader}, * c'est à dire ajouter dans un fichier (du class-path) - * <code>META-INF/services/org.nuiton.topia.replication.TopiaReplicationOperation</code> + * {@code META-INF/services/org.nuiton.topia.replication.TopiaReplicationOperation} * <p/> * une ligne avec le nom qualifie de votre implantation. * <p/> @@ -62,54 +61,38 @@ * Creer et enregister une operation utilisateur apres la creation du * modele via la methode * <p/> - * {@link TopiaReplicationService#prepare(TopiaEntityEnum[], String...)}. + * {@link TopiaReplicationService#prepare(TopiaEntityEnum[],boolean, String...)}. * * @param model le modele de replication * @param ownerNode le noeud proprietaire de l'operation * @param phase la phase ou positionner l'operation * @param parameters les parametres de l'operation + * @throws UnsupportedOperationException if can not register this operation + * (says when operation is only internal) * @see ReplicationModel * @see ReplicationOperationPhase */ void register(ReplicationModel model, ReplicationNode ownerNode, ReplicationOperationPhase phase, - Object... parameters); + Object... parameters) throws UnsupportedOperationException; /** * Execute l'operation avec le parametrage donnee. * <p/> * Note : le commit sur le context cible doit etre geree dans la methode. * - * @param operationDef la definition de l'operation a realiser - * @param srcCtxt le context source - * @param dstCtxt le context destination - * @param entities la liste des - * @param data le dictionnaire de toutes les donnees a repliquer + * @param replicationContext le contexte de replication + * @param operationDef la definition de l'operation a realiser + * @param srcCtxt le context source + * @param dstCtxt le context destination + * @param entities la liste des entités à traiter * @throws Exception pour toute erreur */ - void run(ReplicationOperationDef operationDef, + void run(TopiaReplicationContext replicationContext, + ReplicationOperationDef operationDef, TopiaContextImplementor srcCtxt, TopiaContextImplementor dstCtxt, - List<? extends TopiaEntity> entities, - TopiaEntityIdsMap data) throws Exception; + List<? extends TopiaEntity> entities) throws Exception; - /** - * Execute l'operation inverse avec le parametrage donnee - * (pour annuler l'opération). - * <p/> - * Note : le commit sur le context cible doit etre geree dans la methode. - * - * @param operationDef la definition de l'operation a realiser - * @param srcCtxt le context source - * @param dstCtxt le context destination - * @param entities la liste des - * @param data le dictionnaire de toutes les donnees a repliquer - * @throws Exception pour toute erreur - */ - void rollback(ReplicationOperationDef operationDef, - TopiaContextImplementor srcCtxt, - TopiaContextImplementor dstCtxt, - List<? extends TopiaEntity> entities, - TopiaEntityIdsMap data) throws Exception; } Deleted: trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationOperationProvider.java =================================================================== --- trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationOperationProvider.java 2010-08-21 16:02:52 UTC (rev 2104) +++ trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationOperationProvider.java 2010-08-21 16:05:12 UTC (rev 2105) @@ -1,53 +0,0 @@ -/* - * #%L - * ToPIA :: Service Replication - * - * $Id$ - * $HeadURL$ - * %% - * Copyright (C) 2004 - 2010 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 - * <http://www.gnu.org/licenses/lgpl-3.0.html>. - * #L% - */ -package org.nuiton.topia.replication; - -import java.util.ServiceLoader; - -/** - * Provider of {@link TopiaReplicationOperation}. - * - * @author tchemit <chemit@codelutin.com> - * @since 2.4.3 - */ -public interface TopiaReplicationOperationProvider { - - /** - * Obtains all {@link TopiaReplicationOperation} available - * via {@link ServiceLoader}. - * - * @return the array of available operations - */ - TopiaReplicationOperation[] getOperations(); - - /** - * Obtains the instanciated (and initialized) operation of the given type. - * - * @param operationClass type of searched operation - * @return the found operation, or {@code null} if not found. - */ - TopiaReplicationOperation getOperation( - Class<? extends TopiaReplicationOperation> operationClass); -} Copied: trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationOperationProvider.java (from rev 2101, trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationOperationProviderImpl.java) =================================================================== --- trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationOperationProvider.java (rev 0) +++ trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationOperationProvider.java 2010-08-21 16:05:12 UTC (rev 2105) @@ -0,0 +1,128 @@ +/* + * #%L + * ToPIA :: Service Replication + * + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2010 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 + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topia.replication; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.topia.persistence.util.TopiaEntityHelper; +import org.nuiton.topia.replication.model.ReplicationOperationDef; + +import java.util.ArrayList; +import java.util.List; +import java.util.ServiceLoader; + +/** + * Provider of {@link TopiaReplicationOperation}. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.4.3 + */ +public class TopiaReplicationOperationProvider { + + /** Logger */ + private static final Log log = + LogFactory.getLog(TopiaReplicationOperationProvider.class); + + /** + * All available operations detected via a {@link ServiceLoader} on + * contract {@link TopiaReplicationOperation}. + */ + protected TopiaReplicationOperation[] operations; + + /** + * Obtains all {@link TopiaReplicationOperation} available + * via {@link ServiceLoader}. + * <p/> + * If {@link #operations} is null, then load operations, otherwise just + * return the already computed result. + * + * @return the array of all available operations + */ + public TopiaReplicationOperation[] getOperations() { + if (operations == null) { + // chargement des operations disponibles une seule fois + + ServiceLoader<TopiaReplicationOperation> loader = + ServiceLoader.load(TopiaReplicationOperation.class); + + List<TopiaReplicationOperation> operations = + new ArrayList<TopiaReplicationOperation>(); + + for (TopiaReplicationOperation op : loader) { + if (log.isDebugEnabled()) { + log.debug("detected operation " + op); + } + operations.add(op); + } + this.operations = operations.toArray( + new TopiaReplicationOperation[operations.size()]); + } + return operations; + } + + /** + * Obtains the instanciated (and initialized) operation of the given type. + * + * @param operationClass type of searched operation + * @return the found operation, or {@code null} if not found. + */ + public TopiaReplicationOperation getOperation( + Class<? extends TopiaReplicationOperation> operationClass) { + + TopiaEntityHelper.checkNotNull("getOperation", "operationClass", + operationClass); + TopiaReplicationOperation result = null; + + for (TopiaReplicationOperation op : getOperations()) { + if (operationClass.isAssignableFrom(op.getClass())) { + result = op; + break; + } + } + return result; + } + + /** + * Obtains the instanciated (and initialized) operation of the given + * operation definition. + * + * @param operationDef operation definition of searched operation + * @return the found operation, or {@code null} if not found. + */ + public TopiaReplicationOperation getOperation(ReplicationOperationDef operationDef) { + TopiaEntityHelper.checkNotNull("getOperation", "operationDef", + operationDef); + return getOperation(operationDef.getOperationClass()); + } + + public TopiaReplicationOperationUndoable getUndoableOperation(ReplicationOperationDef operationDef) throws IllegalArgumentException { + TopiaReplicationOperation operation = getOperation(operationDef); + if (!(operation instanceof TopiaReplicationOperationUndoable)) { + throw new IllegalArgumentException( + "the operation " + operation + " is not undoable"); + } + return (TopiaReplicationOperationUndoable) operation; + } +} Deleted: trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationOperationProviderImpl.java =================================================================== --- trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationOperationProviderImpl.java 2010-08-21 16:02:52 UTC (rev 2104) +++ trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationOperationProviderImpl.java 2010-08-21 16:05:12 UTC (rev 2105) @@ -1,92 +0,0 @@ -/* - * #%L - * ToPIA :: Service Replication - * - * $Id$ - * $HeadURL$ - * %% - * Copyright (C) 2004 - 2010 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 - * <http://www.gnu.org/licenses/lgpl-3.0.html>. - * #L% - */ -package org.nuiton.topia.replication; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.nuiton.topia.persistence.util.TopiaEntityHelper; - -import java.util.ArrayList; -import java.util.List; -import java.util.ServiceLoader; - -/** - * Default implementation of {@link TopiaReplicationOperationProvider}. - * - * @author tchemit <chemit@codelutin.com> - * @since 2.4.3 - */ -public class TopiaReplicationOperationProviderImpl implements TopiaReplicationOperationProvider { - - /** Logger */ - private static final Log log = - LogFactory.getLog(TopiaReplicationOperationProviderImpl.class); - - /** - * la liste des operations disponibles (chargee automatiquement via un - * ServiceLoader sur le contract {@link TopiaReplicationOperation}) - */ - protected TopiaReplicationOperation[] operations; - - @Override - public TopiaReplicationOperation[] getOperations() { - if (operations == null) { - // chargement des operations disponibles une seule fois - - ServiceLoader<TopiaReplicationOperation> loader = - ServiceLoader.load(TopiaReplicationOperation.class); - - List<TopiaReplicationOperation> operations = - new ArrayList<TopiaReplicationOperation>(); - - for (TopiaReplicationOperation op : loader) { - if (log.isDebugEnabled()) { - log.debug("detected operation " + op); - } - operations.add(op); - } - this.operations = operations.toArray( - new TopiaReplicationOperation[operations.size()]); - } - return operations; - } - - @Override - public TopiaReplicationOperation getOperation( - Class<? extends TopiaReplicationOperation> operationClass) { - - TopiaEntityHelper.checkNotNull("getOperation", "operationClass", - operationClass); - TopiaReplicationOperation result = null; - - for (TopiaReplicationOperation op : getOperations()) { - if (operationClass.isAssignableFrom(op.getClass())) { - result = op; - break; - } - } - return result; - } -} Added: trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationOperationUndoable.java =================================================================== --- trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationOperationUndoable.java (rev 0) +++ trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationOperationUndoable.java 2010-08-21 16:05:12 UTC (rev 2105) @@ -0,0 +1,56 @@ +/* + * #%L + * ToPIA :: Service Replication + * + * $Id: TopiaReplicationOperation.java 2098 2010-08-14 13:54:40Z tchemit $ + * $HeadURL: http://svn.nuiton.org/svn/topia/trunk/topia-service-replication/src/main/jav... $ + * %% + * Copyright (C) 2004 - 2010 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 + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.replication; + +import org.nuiton.topia.framework.TopiaContextImplementor; +import org.nuiton.topia.replication.model.ReplicationOperationDef; + +/** + * Le contrat d'une operation {@link TopiaReplicationOperation} qui peut être + * rollbacker lorsque la replication a échouée. + * <p/> + * + * @author tchemit <chemit@codelutin.com> + * @since 2.4.3 + */ +public interface TopiaReplicationOperationUndoable extends TopiaReplicationOperation { + + /** + * Execute l'operation inverse avec le parametrage donnee + * (pour annuler l'opération). + * <p/> + * Note : le commit sur le context cible doit etre geree dans la methode. + * + * @param operationDef la definition de l'operation a realiser + * @param replicationContext le context de replication + * @param dstCtxt le context destination + * @throws Exception pour toute erreur + */ + void rollback(ReplicationOperationDef operationDef, + TopiaReplicationContext replicationContext, + TopiaContextImplementor dstCtxt + ) throws Exception; +} Modified: trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationService.java =================================================================== --- trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationService.java 2010-08-21 16:02:52 UTC (rev 2104) +++ trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationService.java 2010-08-21 16:05:12 UTC (rev 2105) @@ -28,12 +28,8 @@ import org.nuiton.topia.TopiaException; import org.nuiton.topia.framework.TopiaService; import org.nuiton.topia.persistence.TopiaEntityEnum; -import org.nuiton.topia.persistence.util.TopiaEntityIdsMap; import org.nuiton.topia.replication.model.ReplicationModel; -import org.nuiton.topia.replication.model.ReplicationNode; -import java.util.List; - /** * User visible interface for replication engin * <p/> @@ -43,8 +39,7 @@ * <p/> * The replication is always done in two phases : * <p/> - * <li> - prepare the replication model - ({@link #prepare(TopiaEntityEnum[], - * String...)} - ({@link #prepareForAll(TopiaEntityEnum[])} + * <li> - prepare the replication model - ({@link #prepare(TopiaEntityEnum[],boolean, String...)} - ({@link #prepareForAll(TopiaEntityEnum[])} * <p/> * <li> - lanch replication {@link #doReplicate(ReplicationModel, * TopiaContext)} @@ -60,21 +55,48 @@ /** Nom du service topia */ String TOPIA_SERVICE_NAME = "topia.service.replication"; + /** + * Obtains the {@code model builder} use to creat the replication's model. + * + * @return the model builder + * @since 2.4.3 + */ TopiaReplicationModelBuilder getModelBuilder(); /** * Prepare le modele de replication pour les entites dans les topiaIds sont * donnes. * - * @param contracts les contrats a repliquer - * @param topiaIds la liste des ids d'entites a repliquer + * @param contracts les contrats a repliquer + * @param computeOrder drapeau positionné à {@code true} si on doit calculer + * l'ordre des entités à repliquer, sinon on utilise + * l'ordre induit par les {@code contracts}. + * @param topiaIds la liste des ids d'entites a repliquer * @return le model de replication initialise * @throws TopiaException pour toute erreur recontree */ ReplicationModel prepare(TopiaEntityEnum[] contracts, + boolean computeOrder, String... topiaIds) throws TopiaException; /** + * Prepare le modele de replication pour les entites dans les topiaIds sont + * donnes en utilisant l'ordre des {@code contracts} donné pour l'ordre de + * replication. + * + * @param contracts les contrats a repliquer + * l'ordre des entités à repliquer, sinon on utilise + * l'ordre induit par les {@code contracts}. + * @param topiaIds la liste des ids d'entites a repliquer + * @return le model de replication initialise + * @throws TopiaException pour toute erreur recontree + * @deprecated since 2.4.3, prefer use the method {@link #prepare(TopiaEntityEnum[], boolean, String...)} with flag setted to {@code false}. + */ + @Deprecated + ReplicationModel prepareWithComputedOrder(TopiaEntityEnum[] contracts, + String... topiaIds) throws TopiaException; + + /** * Prepare le modele de replication pour toutes les entites des types * donnes. * <p/> @@ -92,27 +114,15 @@ ReplicationModel prepareForAll(TopiaEntityEnum[] contracts) throws TopiaException; /** - * Prepare le modele de replication pour les entites données en ne faisant - * pas de calcul sur l'ordre des entités à répliquer. - * <p/> - * L'ordre des types donnees sera celui utilisé. - * - * @param contracts les contrats a repliquer (dans l'ordre donnée) - * @param topiaIds les ids des entites a repliquer - * @return le model de replication initialise - * @throws TopiaException pour toute erreur recontree - */ - ReplicationModel prepareWithComputedOrder(TopiaEntityEnum[] contracts, - String... topiaIds) throws TopiaException; - - /** * Ajouter une nouvelle operation pre-replication, sur un type de donnee. * * @param model le modele de replication * @param type le type du noeud de replication * @param operationClass l'implantation de l'operation * @param parameters les parametres supplementaires pour l'operation + * @deprecated since 2.4.3, prefer use the method {@link TopiaReplicationModelBuilder#addBeforeOperation(ReplicationModel, TopiaEntityEnum, Class, Object...)} */ + @Deprecated void addBeforeOperation(ReplicationModel model, TopiaEntityEnum type, Class<? extends TopiaReplicationOperation> operationClass, @@ -125,7 +135,9 @@ * @param type le type du noeud de replication * @param operationClass l'implantation de l'operation * @param parameters les parametres supplementaires pour l'operation + * @deprecated since 2.4.3, prefer use the method {@link TopiaReplicationModelBuilder#addAfterOperation(ReplicationModel, TopiaEntityEnum, Class, Object...)} */ + @Deprecated void addAfterOperation(ReplicationModel model, TopiaEntityEnum type, Class<? extends TopiaReplicationOperation> operationClass, @@ -133,9 +145,9 @@ /** * Lance l'operation de replication a partir du context source vers le - * context de destination sur les entites donnees et en utilisant le model + * context de destination sur les entites donnees et en utilisant le modele * de replication precedemment construit via la methode {@link - * #prepare(TopiaEntityEnum[], String...)} ou {@link + * #prepare(TopiaEntityEnum[],boolean, String...)} ou {@link * #prepareForAll(TopiaEntityEnum[])}. * * @param model le modele de replication @@ -148,16 +160,10 @@ /** * Pour revenir en arrière lorsque la réplication a échouée . * - * @param model le modele de replication - * @param data le dictionnaire des données à repliquer par type d'entités - * @param nodes les nodes déjà traités que l'on doit reverter - * @param dstCtxt le context sur la source de donnees ou repliquer + * @param replicationContext the replication's context used to start replication. * @throws Exception pour toute erreur pendant la replication */ - void doRollback(ReplicationModel model, - TopiaEntityIdsMap data, - List<ReplicationNode> nodes, - TopiaContext dstCtxt) throws Exception; + void doRollback(TopiaReplicationContext replicationContext) throws Exception; } Modified: trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationServiceImpl.java =================================================================== --- trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationServiceImpl.java 2010-08-21 16:02:52 UTC (rev 2104) +++ trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationServiceImpl.java 2010-08-21 16:05:12 UTC (rev 2105) @@ -33,12 +33,10 @@ import org.nuiton.topia.persistence.TopiaEntity; import org.nuiton.topia.persistence.TopiaEntityEnum; import org.nuiton.topia.persistence.util.TopiaEntityHelper; -import org.nuiton.topia.persistence.util.TopiaEntityIdsMap; import org.nuiton.topia.replication.model.ReplicationModel; import org.nuiton.topia.replication.model.ReplicationNode; import org.nuiton.topia.replication.model.ReplicationOperationDef; -import java.util.ArrayList; import java.util.List; /** @@ -50,11 +48,17 @@ public class TopiaReplicationServiceImpl implements TopiaReplicationService { /** Logger */ - private static final Log log = LogFactory.getLog(TopiaReplicationServiceImpl.class); + private static final Log log = + LogFactory.getLog(TopiaReplicationServiceImpl.class); /** le contexte sur la base source de la replication */ protected TopiaContextImplementor context; + /** + * le constructeur de modèle de réplication. + * + * @since 2.4.3 + */ protected TopiaReplicationModelBuilder modelBuilder; //-------------------------------------------------------------------------- @@ -98,38 +102,23 @@ @Override public ReplicationModel prepare(TopiaEntityEnum[] contracts, - String... entities) throws TopiaException { - ReplicationModel model = getModelBuilder().prepare(context, contracts, entities); + boolean computeOrder, + String... topiaIds) throws TopiaException { + ReplicationModel model = + getModelBuilder().prepare(context, contracts, computeOrder, topiaIds); return model; -// ReplicationModel model = -// createModel(contracts, entities); -// -// initModel(model, true); -// -// return model; } @Override - public ReplicationModel prepareForAll(TopiaEntityEnum[] contracts) throws TopiaException { - ReplicationModel model = getModelBuilder().prepareForAll(contracts); - return model; -// ReplicationModel model = createModelForAll(contracts); -// -// initModel(model, true); -// -// return model; + public ReplicationModel prepareWithComputedOrder(TopiaEntityEnum[] contracts, + String... topiaIds) throws TopiaException { + return prepare(contracts, false, topiaIds); } @Override - public ReplicationModel prepareWithComputedOrder(TopiaEntityEnum[] contracts, - String... topiaIds) throws TopiaException { - ReplicationModel model = getModelBuilder().prepareWithComputedOrder(contracts); + public ReplicationModel prepareForAll(TopiaEntityEnum[] contracts) throws TopiaException { + ReplicationModel model = getModelBuilder().prepareForAll(contracts); return model; -// ReplicationModel model = createModelWithComputedOrder(contracts, topiaIds); -// -// initModel(model, false); -// -// return model; } @Override @@ -137,13 +126,12 @@ TopiaEntityEnum type, Class<? extends TopiaReplicationOperation> operationClass, Object... parameters) { - getModelBuilder().addBeforeOperation(model, type, operationClass, parameters); -// createOperation(model, -// type, -// ReplicationOperationPhase.before, -// operationClass, -// parameters -// ); + + getModelBuilder().addBeforeOperation(model, + type, + operationClass, + parameters + ); } @Override @@ -151,425 +139,209 @@ TopiaEntityEnum type, Class<? extends TopiaReplicationOperation> operationClass, Object... parameters) { - getModelBuilder().addAfterOperation(model, type, operationClass, parameters); -// createOperation(model, -// type, -// ReplicationOperationPhase.after, -// operationClass, -// parameters -// ); + + getModelBuilder().addAfterOperation(model, + type, + operationClass, + parameters + ); } @Override + public TopiaReplicationModelBuilder getModelBuilder() { + if (modelBuilder == null) { + modelBuilder = new TopiaReplicationModelBuilder(); + } + return modelBuilder; + } + + @Override public void doReplicate(ReplicationModel model, - TopiaContext dstCtxt) throws Exception { + TopiaContext targetTx) throws Exception { TopiaEntityHelper.checkNotNull("doReplicate", "model", model); - TopiaEntityHelper.checkNotNull("doReplicate", "dstCtxt", dstCtxt); + TopiaEntityHelper.checkNotNull("doReplicate", "dstCtxt", targetTx); - TopiaContextImplementor srcCtxt = null; + // create a replication context + TopiaReplicationContext replicationContext = + new TopiaReplicationContext(getModelBuilder().getOperationProvider(), + model, + context, + targetTx); - TopiaEntityIdsMap data = null; + // init replication context + replicationContext.init(); - // keep the list of treated nodes (to roolback them if something - // is wrong). - List<ReplicationNode> treated = new ArrayList<ReplicationNode>(); try { - srcCtxt = (TopiaContextImplementor) context.beginTransaction(); - - data = getIds(model, srcCtxt); - - srcCtxt.closeContext(); - - model.adjustOperations(data); - - //FIXME tchemit 2077-07-26, should use next line when - // http://nuiton.org/issues/show/779 is closed - boolean needTransaction = true; -// boolean needTransaction = ((TopiaContextImplementor) dstCtxt).getRootContext().equals(dstCtxt); - - TopiaContext tx; - for (ReplicationNode node : model.getOrder()) { - srcCtxt = (TopiaContextImplementor) context.beginTransaction(); + // start replication of current node + doReplicateNode(replicationContext, node); - if (needTransaction) { - tx = dstCtxt.beginTransaction(); - } else { - tx = dstCtxt; - } - try { - doReplicateNode(node, srcCtxt, tx, data, treated); - } catch (Exception e) { + // node was sucessfull replicated, mark it + replicationContext.addTreatedNode(node); + } + } catch (Exception e) { - // un erreur est survenu sur le noeud de réplication - // on rollback la transaction de ce noeud - try { - tx.rollbackTransaction(); - } finally { + // an error occurs, rollback all sucessfull replicated nodes. + doRollback(replicationContext); - // on revert les noeuds deja commite - doRollback(model, data, treated, dstCtxt); - } - - throw e; - } finally { - srcCtxt.closeContext(); - if (needTransaction) { - // on doit fermer la transaction car on l'a ouverte dans - // cette méthode, sinon c'est de la responsabilite du - // code appellant - tx.closeContext(); - } - } - } -// } catch (Exception e) { -// dstCtxt.rollbackTransaction(); -// throw e; + // then throw the original error + throw e; } finally { - if (data != null) { - data.clear(); - } - if (treated != null) { - treated.clear(); - } - // on ne doit jamais commiter sur la base source - if (srcCtxt != null && !srcCtxt.isClosed()) { - srcCtxt.rollbackTransaction(); - srcCtxt.closeContext(); - } -// dstCtxt.closeContext(); + replicationContext.clear(); } } @Override - public void doRollback(ReplicationModel model, - TopiaEntityIdsMap data, - List<ReplicationNode> nodes, - TopiaContext dstCtxt) throws Exception { - if (nodes.isEmpty()) { + 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; } - TopiaContextImplementor srcCtxt; + log.info("Will rollback " + treated.length + " nodes..."); - TopiaContext tx; + for (ReplicationNode node : treated) { - boolean needTransaction = true; - - log.info("Will rollback " + nodes.size() + " nodes..."); - - for (ReplicationNode node : nodes) { - - log.info("Rollback node " + node); - srcCtxt = (TopiaContextImplementor) context.beginTransaction(); - - if (needTransaction) { - tx = dstCtxt.beginTransaction(); - } else { - tx = dstCtxt; - } try { - doRollbackNode(node, srcCtxt, tx, data); + doRollbackNode(replicationContext, node); } catch (Exception e) { - // un erreur est survenu sur le noeud de réplication - // on rollback la transaction de ce noeud + log.error("Could not rollback node " + node, e); - tx.rollbackTransaction(); - - } finally { - srcCtxt.rollbackTransaction(); - srcCtxt.closeContext(); - - if (needTransaction) { - // on doit fermer la transaction car on l'a ouverte dans - // cette méthode, sinon c'est de la responsabilite du - // code appellant - tx.closeContext(); - } + //tchemit 2010-08-17 perharps we should not throw the exception + // and try to rollback other nodes ? + throw e; } - } } - @Override - public TopiaReplicationModelBuilder getModelBuilder() { - if (modelBuilder == null) { - modelBuilder = new TopiaReplicationModelBuilderImpl(); - } - return modelBuilder; - } + public void doReplicateNode(TopiaReplicationContext replicationContext, + ReplicationNode node) throws Exception { - //-------------------------------------------------------------------------- - //-- TopiaReplicationImplementor implementation ---------------------------- - //-------------------------------------------------------------------------- + ReplicationOperationDef[] defs = node.getOperations(); -// @Override -// public ReplicationModel createModel(TopiaEntityEnum[] contracts, -// String... topiaIds) -// throws TopiaException { -// -// Set<Class<? extends TopiaEntity>> detectTypes = -// detectTypes(contracts, topiaIds); -// ReplicationModel model = -// new ReplicationModel(contracts, detectTypes, topiaIds); -// return model; -// } -// -// @Override -// public ReplicationModel createModelWithComputedOrder( -// TopiaEntityEnum[] contracts, String... topiaIds) -// throws TopiaException { -// ReplicationModel model = -// new ReplicationModel(contracts, false, topiaIds); -// return model; -// } -// -// @Override -// public ReplicationModel createModelForAll(TopiaEntityEnum[] contracts) -// throws TopiaException { -// ReplicationModel model = new ReplicationModel(contracts, true); -// return model; -// } -// -// @Override -// public ReplicationModel initModel(ReplicationModel model, -// boolean computeOrder) -// throws TopiaException { -// checkNotNull("initModel", "model", model); -// -// model.detectAssociations(); -// model.detectDirectDependencies(); -// if (computeOrder) { -// model.detectShell(); -// model.detectDependencies(); -// } -// model.detectObjectsToDettach(); -// model.detectOperations(); -// return model; -// } -// -// @Override -// public TopiaReplicationOperation getOperation( -// Class<? extends TopiaReplicationOperation> operationClass) { -// checkNotNull("getOperation", "operationClass", operationClass); -// -// if (operations == null) { -// throw new IllegalStateException("service was not init!"); -// } -// TopiaReplicationOperation result = null; -// -// for (TopiaReplicationOperation op : operations) { -// if (operationClass.isAssignableFrom(op.getClass())) { -// result = op; -// break; -// } -// } -// return result; -// } -// -// @Override -// public void createOperation(ReplicationModel model, -// TopiaEntityEnum type, -// ReplicationOperationPhase phase, -// Class<? extends TopiaReplicationOperation> operationClass, -// Object... parameters) { -// -// checkNotNull("createOperation", "model", model); -// checkNotNull("createOperation", "type", type); -// checkNotNull("createOperation", "phase", phase); -// checkNotNull("createOperation", "operationClass", operationClass); -// -// TopiaReplicationOperation operation = getOperation(operationClass); -// -// if (operation == null) { -// throw new IllegalArgumentException( -// _("topia.replication.engine.error.unkown.operation", -// operationClass.getSimpleName(), -// Arrays.toString(operations)) -// ); -// } -// -// ReplicationNode node = model.getNode(type); -// if (node == null) { -// throw new IllegalArgumentException( -// _("topia.replication.engine.error.unkown.owner.node", -// type, -// operationClass.getSimpleName(), -// model.getNodes()) -// ); -// } -// operation.register(model, node, phase, parameters); -// } - -// @Override - - public TopiaEntityIdsMap getIds(ReplicationModel model, - TopiaContextImplementor srcCtxt) - throws TopiaException { - - TopiaEntityIdsMap data; - - // on recupere les objets a repliquer par type - if (model.isReplicateAll()) { - - // on recupere pour chaque type tous les ids des entites a repliquer - data = new TopiaEntityIdsMap(); - for (TopiaEntityEnum e : model.getContracts()) { - List<String> ids = srcCtxt.getDAO(e.getContract()).findAllIds(); - data.put(e.getContract(), ids); - } - } else { - - // on recupere les entites specifies a repliquer - TopiaEntity[] entities = TopiaEntityHelper.getEntities(srcCtxt, - model.getTopiaIds() - ); - - // on calcule toutes les ids des entites a repliquer - data = TopiaEntityHelper.detectEntityIds(model.getContracts(), - model.getTypes(), - entities - ); + if (defs.length == 0) { + log.info("skip node " + node + " - no operation detected."); + return; } - return data; - } -// protected void checkServiceIsInit() { -// if (modelBuilder == null) { -// throw new IllegalStateException("service was not init!"); -// } -// } + // destination transaction will be opened at the last moment to avoid + // to open it if something was wrong before... + TopiaContextImplementor dstCtxt = null; -// @Override + // 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 + TopiaContextImplementor srcCtxt = replicationContext.newSourceTx(); - public void doReplicateNode( - ReplicationNode node, - TopiaContext srcCtxt, - TopiaContext dstCtxt, - TopiaEntityIdsMap data, - List<ReplicationNode> treated) throws Exception { - - // on trie toujours les operations a realiser selon leur phase - // (avant ou apres la duplication) - node.sortOperations(); - - List<ReplicationOperationDef> operationDefs = node.getOperations(); - - if (operationDefs.isEmpty()) { - log.info("skip node " + node + " - no operation detected."); - } else { - - log.info("start for " + node + " : " + operationDefs.size() + + try { + log.info("start replication for " + node + " : " + defs.length + " operation(s)"); - List<String> nodeEntityIds = - data.get(node.getContract().getContract()); + // 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 " + nodeEntityIds.size() + + log.info("will replicate on " + entities.size() + " entity(ies)"); } if (log.isDebugEnabled()) { - for (String id : nodeEntityIds) { - log.debug(id); + for (TopiaEntity entity : entities) { + log.debug(entity.getTopiaId()); } } - List<? extends TopiaEntity> nodeEntities = TopiaEntityHelper.getEntitiesList( - srcCtxt, - nodeEntityIds.toArray(new String[nodeEntityIds.size()]) - ); + // 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 operationDef : operationDefs) { + for (ReplicationOperationDef def : defs) { - log.info("start " + operationDef); + log.info("start operation " + def); TopiaReplicationOperation operation = - getModelBuilder().getOperation(operationDef.getOperationClass()); + replicationContext.getOperation(def); - operation.run(operationDef, - (TopiaContextImplementor) srcCtxt, - (TopiaContextImplementor) dstCtxt, - nodeEntities, - data + 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); + } } - // 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...) - srcCtxt.rollbackTransaction(); } - - treated.add(node); } + public void doRollbackNode(TopiaReplicationContext replicationContext, + ReplicationNode node) throws Exception { -// @Override + // recuperation des operations reversibles + ReplicationOperationDef[] defs = node.getUndoableOperations(); - public void doRollbackNode( - ReplicationNode node, - TopiaContext srcCtxt, - TopiaContext dstCtxt, - TopiaEntityIdsMap data) throws Exception { + if (defs.length == 0) { - // on trie toujours les operations a realiser selon leur phase - // (avant ou apres la duplication) - node.sortOperations(); + // pas d'operation reversible, donc rien a faire sur ce noeud. + log.info("skip node " + node + " - no reversible operation detected."); + return; + } - List<ReplicationOperationDef> operationDefs = node.getOperations(); + log.info("start rollback for " + node + " : " + defs.length + + " reversible operation(s)"); - if (operationDefs.isEmpty()) { - log.info("skip node " + node + " - no operation detected."); - } else { + // open transaction on target db + TopiaContextImplementor dstCtxt = replicationContext.newTargetTx(); - log.info("start for " + node + " : " + operationDefs.size() + - " operation(s)"); + try { - List<String> nodeEntityIds = - data.get(node.getContract().getContract()); - if (log.isInfoEnabled()) { - log.info("will replicate on " + nodeEntityIds.size() + - " entity(ies)"); - } - if (log.isDebugEnabled()) { - for (String id : nodeEntityIds) { - log.debug(id); - } - } + for (ReplicationOperationDef def : defs) { - List<? extends TopiaEntity> nodeEntities = TopiaEntityHelper.getEntitiesList( - srcCtxt, - nodeEntityIds.toArray(new String[nodeEntityIds.size()]) - ); + log.info("start rollback operation " + def); - for (ReplicationOperationDef operationDef : operationDefs) { - - log.info("start " + operationDef); - - TopiaReplicationOperation operation = - getModelBuilder().getOperation(operationDef.getOperationClass()); - - operation.rollback(operationDef, - (TopiaContextImplementor) srcCtxt, - (TopiaContextImplementor) dstCtxt, - nodeEntities, - data - ); - + TopiaReplicationOperationUndoable operation = + replicationContext.getUndoableOperation(def); + operation.rollback(def, replicationContext, dstCtxt); } - // 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...) - srcCtxt.rollbackTransaction(); + } 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); + } } } Modified: trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/model/ReplicationLink.java =================================================================== --- trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/model/ReplicationLink.java 2010-08-21 16:02:52 UTC (rev 2104) +++ trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/model/ReplicationLink.java 2010-08-21 16:05:12 UTC (rev 2105) @@ -98,7 +98,7 @@ * * @param universe l'univers des noeuds disponibles * @param currentNode le noeud qui vient d'etre replique - * @return <code>true</code> si on peut reattacher ce lien + * @return {@code true} si on peut reattacher ce lien */ public boolean canReattach(Set<ReplicationNode> universe, ReplicationNode currentNode) { @@ -111,6 +111,7 @@ @Override public String toString() { - return "<source:" + source + ", target:" + target + ", name:" + name + ", association:" + association + ">"; + return "<source:" + source + ", target:" + target + ", name:" + name + + ", association:" + association + ">"; } } Modified: trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/model/ReplicationModel.java =================================================================== --- trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/model/ReplicationModel.java 2010-08-21 16:02:52 UTC (rev 2104) +++ trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/model/ReplicationModel.java 2010-08-21 16:05:12 UTC (rev 2105) @@ -25,6 +25,7 @@ package org.nuiton.topia.replication.model; +import org.apache.commons.collections.CollectionUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.nuiton.topia.TopiaException; @@ -44,14 +45,20 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; /** - * Model of a replication operation. + * Model of replication. + * <p/> + * the object contains the required {@link #nodes} to replicate : + * <ul> + * <li>all entities for the given {@code nodes} if flag {@link #replicateAll} is + * setted to {@code true}</li> + * <li>the entities given by the field {@link #topiaIds}</li> + * </ul> * * @author tchemit <chemit@codelutin.com> * @since 2.2.0 @@ -62,55 +69,69 @@ private static final Log log = LogFactory.getLog(ReplicationModel.class); /** l'ensemble des contrats d'entites a repliquer */ - final protected TopiaEntityEnum[] contracts; + protected final TopiaEntityEnum[] contracts; /** les ids des entites a repliquer (non utilise en mode replicateAll) */ - final protected String[] topiaIds; + protected final String[] topiaIds; /** le dictionnaire des noeuds a repliquer associes a leur type */ - protected Map<Class<? extends TopiaEntity>, ReplicationNode> nodes; + protected final Map<TopiaEntityEnum, ReplicationNode> nodes; /** la liste des noeuds a repliquer (dans l'ordre de replication) */ - protected List<ReplicationNode> order; + protected final List<ReplicationNode> order; /** - * un drapeau pour savoir si on effectue une replication de toutes les donnees - * des contrats. + * un drapeau pour savoir si on effectue une replication de toutes les + * donnees des contrats. */ - final protected boolean replicateAll; + protected final boolean replicateAll; - public ReplicationModel(TopiaEntityEnum[] contracts, Set<Class<? extends TopiaEntity>> types, String... topiaIds) { + public ReplicationModel(TopiaEntityEnum[] contracts, + Set<Class<? extends TopiaEntity>> types, + String... topiaIds) { this.contracts = contracts; this.topiaIds = topiaIds; replicateAll = false; - nodes = new LinkedHashMap<Class<? extends TopiaEntity>, ReplicationNode>(); order = new ArrayList<ReplicationNode>(); - Map<Class<? extends TopiaEntity>, ReplicationNode> tmpNodes = new HashMap<Class<? extends TopiaEntity>, ReplicationNode>(); + Map<TopiaEntityEnum, ReplicationNode> tmpNodes = + new HashMap<TopiaEntityEnum, ReplicationNode>(); for (Class<? extends TopiaEntity> k : types) { - TopiaEntityEnum e = TopiaEntityHelper.getEntityEnum(k, contracts); + TopiaEntityEnum e = getContract(k); ReplicationNode replicationNode = new ReplicationNode(e); - tmpNodes.put(k, replicationNode); + tmpNodes.put(e, replicationNode); } nodes = Collections.unmodifiableMap(tmpNodes); } - public ReplicationModel(TopiaEntityEnum[] contracts, boolean replicateAll, String... topiaIds) { + public ReplicationModel(TopiaEntityEnum[] contracts, + boolean replicateAll, + String... topiaIds) { this.contracts = contracts; this.topiaIds = topiaIds; this.replicateAll = replicateAll; - nodes = new LinkedHashMap<Class<? extends TopiaEntity>, ReplicationNode>(); order = new ArrayList<ReplicationNode>(); - Map<Class<? extends TopiaEntity>, ReplicationNode> tmpNodes = new HashMap<Class<? extends TopiaEntity>, ReplicationNode>(); + Map<TopiaEntityEnum, ReplicationNode> tmpNodes = + new HashMap<TopiaEntityEnum, ReplicationNode>(); for (TopiaEntityEnum e : contracts) { ReplicationNode replicationNode = new ReplicationNode(e); - tmpNodes.put(e.getContract(), replicationNode); + tmpNodes.put(e, replicationNode); if (!replicateAll) { - // mode restreint : l'ordre est induit par l'ordre sur les contrats passes + // mode restreint : l'ordre est induit par l'ordre sur les + // contrats passes order.add(replicationNode); } } nodes = Collections.unmodifiableMap(tmpNodes); + } + @SuppressWarnings({"unchecked"}) + public TopiaEntityEnum getContract(Class<?> type) { + TopiaEntityEnum e = null; + if (TopiaEntity.class.isAssignableFrom(type)) { + e = TopiaEntityHelper.getEntityEnum( + (Class<? extends TopiaEntity>) type, contracts); + } + return e; } public Collection<ReplicationNode> getNodes() { @@ -118,19 +139,30 @@ } public Set<Class<? extends TopiaEntity>> getTypes() { - return nodes.keySet(); + Set<Class<? extends TopiaEntity>> result = + new HashSet<Class<? extends TopiaEntity>>(); + for (TopiaEntityEnum e : nodes.keySet()) { + result.add(e.getContract()); + } + return result; } - public ReplicationNode getNode(Class<? extends TopiaEntity> clazz) { - return nodes.get(clazz); - } - public ReplicationNode getNode(TopiaEntityEnum contract) { - return nodes.get(contract.getContract()); + return nodes.get(contract); } public void addDependency(List<ReplicationNode> nodes) { - order.addAll(nodes); + if (log.isDebugEnabled()) { + log.debug("Try to add nodes : " + nodes + " in universe : " + order); + } + for (ReplicationNode node : nodes) { + if (order.contains(node)) { + // can not be done + throw new IllegalStateException( + "Node " + node + " is already registred : " + order); + } + order.add(node); + } } public TopiaEntityEnum[] getContracts() { @@ -152,11 +184,11 @@ @SuppressWarnings("unchecked") public ReplicationNode getNode(String propertyName, Class<?> propertyType) { if (TopiaEntity.class.isAssignableFrom(propertyType)) { - //TODO on devrait repasser par le contracts pour etre sur d'etre - //TODO sur d'etre sur une interface d'entite ? - Class<? extends TopiaEntity> t = (Class<? extends TopiaEntity>) propertyType; - if (nodes.containsKey(t)) { - ReplicationNode dep = getNode(t); + Class<? extends TopiaEntity> t = + (Class<? extends TopiaEntity>) propertyType; + TopiaEntityEnum e = getContract(t); + if (nodes.containsKey(e)) { + ReplicationNode dep = getNode(e); return dep; } } @@ -164,17 +196,23 @@ } public void detectAssociations(TopiaEntityEnum... filter) throws TopiaException { - for (Class<? extends TopiaEntity> type : nodes.keySet()) { + for (TopiaEntityEnum type : nodes.keySet()) { ReplicationNode node = getNode(type); EntityOperator<? super TopiaEntity> operator = node.getOperator(); - List<String> associationProperties = operator.getAssociationProperties(); + List<String> associationProperties = + operator.getAssociationProperties(); if (!associationProperties.isEmpty()) { for (String p : associationProperties) { - ReplicationNode dep = getNode(p, operator.getAssociationPropertyType(p)); + ReplicationNode dep = getNode( + p, + operator.getAssociationPropertyType(p) + ); if (dep != null) { if (log.isDebugEnabled()) { - log.debug("from type - " + type.getSimpleName() + " [" + p + ":" + dep + "]"); + log.debug("from type - " + + type.getContract().getSimpleName() + + " [" + p + ":" + dep + "]"); } node.addAssociation(p, dep); } @@ -184,16 +222,19 @@ } public void detectDirectDependencies() throws TopiaException { - for (Class<? extends TopiaEntity> type : nodes.keySet()) { + for (TopiaEntityEnum type : nodes.keySet()) { ReplicationNode node = getNode(type); EntityOperator<? super TopiaEntity> operator = node.getOperator(); List<String> properties = operator.getProperties(); if (!properties.isEmpty()) { for (String p : properties) { - ReplicationNode dep = getNode(p, operator.getPropertyType(p)); + ReplicationNode dep = + getNode(p, operator.getPropertyType(p)); if (dep != null) { if (log.isDebugEnabled()) { - log.debug("from type - " + type.getSimpleName() + " [" + p + ":" + dep + "]"); + log.debug("from type - " + + type.getContract().getSimpleName() + + " [" + p + ":" + dep + "]"); } node.addDependency(p, dep); } @@ -203,18 +244,21 @@ } public void detectDependencies() throws TopiaException { - Set<ReplicationNode> toResolved = new HashSet<ReplicationNode>(nodes.values()); + Set<ReplicationNode> toResolved = + new HashSet<ReplicationNode>(nodes.values()); Set<ReplicationNode> resolved = new HashSet<ReplicationNode>(); - List<Set<ReplicationNode>> levels = new ArrayList<Set<ReplicationNode>>(); + List<Set<ReplicationNode>> levels = + new ArrayList<Set<ReplicationNode>>(); + // premiere passe pour detecter les niveaux de replications - // on ne regarde que les dependences directe et pas les associations + // on ne regarde que les dependences directes et pas les associations // si A -> B alors B doit etre dans un niveau inferieure (i.e replique // avant). //TODO Il faut pouvoir gerer les cycles (pour cela on doit avoir - // un dictionnaire pour les compositions qui peuvent etre nulle - // ainsi on doit etre capable de ne pas tenir compte d'une composition - // si c'est nullable, sinon cela veut dire obligatoirement - // que B doit etre replique avant A... + //TODO un dictionnaire pour les compositions qui peuvent etre nulle + //TODO ainsi on doit etre capable de ne pas tenir compte d'une composition + //TODO si c'est nullable, sinon cela veut dire obligatoirement + //TODO que B doit etre replique avant A... while (!toResolved.isEmpty()) { Set<ReplicationNode> level = new HashSet<ReplicationNode>(); for (ReplicationNode node : toResolved) { @@ -237,20 +281,26 @@ } if (safeLevel.isEmpty()) { // on a detecte un cycle sur les dependences, on ne peut rien faire pour le moment - throw new IllegalStateException("un cycle dans les dependences a été détecté, l\'algorithme necessite plus de donnes... \n niveau courant : " + level + "\n resolus : " + getOrder()); + throw new IllegalStateException( + "un cycle dans les dependences a été détecté, " + + "l\'algorithme necessite plus de donnes... " + + "\n niveau courant : " + level + "\n resolus : " + + getOrder()); } } if (log.isDebugEnabled()) { - log.debug("level [" + levels.size() + "] resolved : " + safeLevel); + log.debug("level [" + levels.size() + "] resolved : " + + safeLevel); } toResolved.removeAll(safeLevel); resolved.addAll(safeLevel); levels.add(safeLevel); level.clear(); } + // seconde passe : on recherche le meilleur ordre possible pour // les types de chaque niveau - // on ne base desormais que sur les associations + // on se base desormais que sur les associations // si A -*> B alors on va essayer de repliquer B avant A // si on detecte un cycle alors on ne peut pas imposer un ordre optimal // et on devra dettacher B de A pendant la replication puis reattacher B @@ -258,7 +308,7 @@ // l'ordre optimal permet de ne pas effectuer // calcul des ordres de replications pour les ensembles de chaque niveau // pour trouver le bon ordre, on travaille sur les couvertures - // des noeuds.; + // des noeuds. HashSet<ReplicationNode> done = new HashSet<ReplicationNode>(); for (Set<ReplicationNode> level : levels) { detectDependenciesOrder(level, done); @@ -281,19 +331,22 @@ } dico.put(n, shell); } - List<Set<ReplicationNode>> levels = new ArrayList<Set<ReplicationNode>>(); + List<Set<ReplicationNode>> levels = + new ArrayList<Set<ReplicationNode>>(); while (!dico.isEmpty()) { if (log.isDebugEnabled()) { log.debug("level [" + levels.size() + "] on " + safeLevel); - for (Entry<ReplicationNode, Set<ReplicationNode>> entry : dico.entrySet()) { - log.debug("node " + entry.getKey() + " : " + entry.getValue()); + for (Entry<ReplicationNode, Set<ReplicationNode>> entry : + dico.entrySet()) { + log.debug("node " + entry.getKey() + " : " + + entry.getValue()); } } - // detection des noeud libres Set<ReplicationNode> free = new HashSet<ReplicationNode>(); - for (Entry<ReplicationNode, Set<ReplicationNode>> e : dico.entrySet()) { + for (Entry<ReplicationNode, Set<ReplicationNode>> e : + dico.entrySet()) { if (e.getValue().isEmpty()) { free.add(e.getKey()); } @@ -303,13 +356,17 @@ // un cycle a ete detectee // on ne peut plus rien predire pour cet ensemble if (log.isWarnEnabled()) { - log.warn("level [" + levels.size() + "] cycle detecte : " + dico.keySet()); + log.warn("level [" + levels.size() + + "] cycle detecte : " + dico.keySet()); } - throw new IllegalStateException("un cycle n'a pas pu etre resoud entre l'ensemble " + dico.keySet()); + throw new IllegalStateException( + "un cycle n'a pas pu etre resoud entre l'ensemble " + + dico.keySet()); } log.info("there is some free node(s) to resolve : " + free); - for (Entry<ReplicationNode, Set<ReplicationNode>> e : dico.entrySet()) { + for (Entry<ReplicationNode, Set<ReplicationNode>> e : + dico.entrySet()) { Set<ReplicationNode> list = e.getValue(); list.removeAll(free); } @@ -341,11 +398,13 @@ // sortants de l'universe deja replique // si oui, alors on marque l'association pour un dettachement if (node.hasAssociation()) { - for (Entry<String, ReplicationNode> e : node.getAssociations().entrySet()) { + for (Entry<String, ReplicationNode> e : + node.getAssociations().entrySet()) { ReplicationNode nodeDst = e.getValue(); if (!universe.contains(nodeDst)) { if (log.isDebugEnabled()) { - log.debug("association to dettach " + e.getKey() + " for " + node); + log.debug("association to dettach " + e.getKey() + + " for " + node); } // association sortant node.addAssociationToDettach(e.getKey()); @@ -356,11 +415,13 @@ //TODO la resolution des conflits sur dependences n'est pas encore //TODO en place if (node.hasDependency()) { - for (Entry<String, ReplicationNode> e : node.getDependencies().entrySet()) { + for (Entry<String, ReplicationNode> e : + node.getDependencies().entrySet()) { ReplicationNode nodeDst = e.getValue(); if (!universe.contains(nodeDst)) { if (log.isDebugEnabled()) { - log.debug("dependency to dettach " + e.getKey() + " for " + node); + log.debug("dependency to dettach " + e.getKey() + + " for " + node); } // association sortant node.addDependencyToDettach(e.getKey()); @@ -380,11 +441,13 @@ for (ReplicationNode node : order) { if (node.hasAssociation()) { - for (Entry<String, ReplicationNode> entry : node.getAssociations().entrySet()) { + for (Entry<String, ReplicationNode> entry : + node.getAssociations().entrySet()) { String name = entry.getKey(); // dans tous les cas, on ajoute un link d'association a reattacher ReplicationNode target = node.getAssociations().get(name); - ReplicationLink link = new ReplicationLink(node, target, name, true); + ReplicationLink link = + new ReplicationLink(node, target, name, true); if (nodes.containsValue(target)) { // on a trouve une association que l'on doit gerer links.add(link); @@ -394,17 +457,22 @@ } else { } } - List<String> associationProperties = node.getOperator().getAssociationProperties(); + List<String> associationProperties = + node.getOperator().getAssociationProperties(); for (String name : associationProperties) { Class<?> associationPropertyType = node.getOperator().getAssociationPropertyType(name); - if (!TopiaEntity.class.isAssignableFrom(associationPropertyType) || - !nodes.containsKey(associationPropertyType)) { + TopiaEntityEnum contract = getContract(associationPropertyType); + if (contract == null || !nodes.containsKey(contract)) { +// if (!TopiaEntity.class.isAssignableFrom(associationPropertyType) || +// !nodes.containsKey(associationPropertyType)) { - ReplicationLink link = new ReplicationLink(node, null, name, true); + ReplicationLink link = + new ReplicationLink(node, null, name, true); linksToLoad.add(link); if (log.isDebugEnabled()) { - log.debug("link to load before replication : " + link); + log.debug("link to load before replication : " + + link); } } } @@ -421,7 +489,6 @@ Set<String> names = node.getAssociationsToDettach(); // operations de dettachement d'association for (String name : names) { -// addOperation(node, node, ReplicationOperationPhase.before, DettachAssociation.class, name); addPreOperation(node, node, DettachAssociation.class, name); } } @@ -438,15 +505,12 @@ // on a des associations a charger avant replication for (ReplicationLink link : tmpLinks) { addPreOperation(node, node, LoadLink.class, link); -// ReplicationOperationDef op = new ReplicationOperationDef(ReplicationOperationPhase.before, LoadLink.class, node, link); -// node.addOperation(op); } linksToLoad.removeAll(links); tmpLinks.clear(); } // operation de duplication -// addOperation(node, node, ReplicationOperationPhase.duplicate, Duplicate.class); addDuplicateOperation(node, node, Duplicate.class); universe.add(node); @@ -462,8 +526,6 @@ // on a trouve des liens a reattacher for (ReplicationLink link : tmpLinks) { addPostOperation(node, node, AttachLink.class, link); -// ReplicationOperationDef op = new ReplicationOperationDef(ReplicationOperationPhase.after, AttachLink.class, node, link); -// node.addOperation(op); } // ces liens ne sont plus a traiter links.removeAll(tmpLinks); @@ -473,22 +535,23 @@ public void adjustOperations(TopiaEntityIdsMap data) { for (TopiaEntityEnum e : getContracts()) { - Class<? extends TopiaEntity> contract = e.getContract(); - List<String> ids = data.get(contract); - ReplicationNode node = getNode(contract); + List<String> ids = data.get(e.getContract()); + ReplicationNode node = getNode(e); if (node == null) { // le noeud n'est pas connu (ce n'est pas normal!) continue; } - if (ids == null || ids.isEmpty()) { - List<ReplicationOperationDef> operations = node.getOperations(); - log.info("skip operations on node " + node + " (no data associated)"); + if (CollectionUtils.isEmpty(ids)) { + ReplicationOperationDef[] operations = node.getOperations(); + log.info("skip operations on node " + node + + " (no data associated)"); for (ReplicationOperationDef op : operations) { log.info(" skip " + op); } - operations.clear(); + node.clearOperations(); + } else { + node.sortOperations(); } - } } @@ -501,7 +564,8 @@ } } - protected void getShell(ReplicationNode node, Set<ReplicationNode> explored) { + protected void getShell(ReplicationNode node, + Set<ReplicationNode> explored) { if (!explored.contains(node)) { explored.add(node); } @@ -525,21 +589,36 @@ ReplicationNode node, Class<? extends TopiaReplicationOperation> operationClass, Object... params) { - addOperation(ownerNode, node, ReplicationOperationPhase.before, operationClass, params); + addOperation(ownerNode, + node, + ReplicationOperationPhase.before, + operationClass, + params + ); } protected void addDuplicateOperation(ReplicationNode ownerNode, ReplicationNode node, Class<? extends TopiaReplicationOperation> operationClass, Object... params) { - addOperation(ownerNode, node, ReplicationOperationPhase.duplicate, operationClass, params); + addOperation(ownerNode, + node, + ReplicationOperationPhase.duplicate, + operationClass, + params + ); } protected void addPostOperation(ReplicationNode ownerNode, ReplicationNode node, Class<? extends TopiaReplicationOperation> operationClass, Object... params) { - addOperation(ownerNode, node, ReplicationOperationPhase.after, operationClass, params); + addOperation(ownerNode, + node, + ReplicationOperationPhase.after, + operationClass, + params + ); } protected void addOperation(ReplicationNode ownerNode, @@ -547,7 +626,9 @@ ReplicationOperationPhase phase, Class<? extends TopiaReplicationOperation> operationClass, Object... params) { - ReplicationOperationDef op = new ReplicationOperationDef(phase, operationClass, node, params); + + ReplicationOperationDef op; + op = new ReplicationOperationDef(phase, operationClass, node, params); ownerNode.addOperation(op); } } Modified: trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/model/ReplicationNode.java =================================================================== --- trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/model/ReplicationNode.java 2010-08-21 16:02:52 UTC (rev 2104) +++ trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/model/ReplicationNode.java 2010-08-21 16:05:12 UTC (rev 2105) @@ -31,10 +31,10 @@ import org.nuiton.topia.persistence.TopiaEntityEnum; import org.nuiton.topia.persistence.util.EntityOperator; import org.nuiton.topia.persistence.util.EntityOperatorStore; +import org.nuiton.topia.replication.TopiaReplicationOperationUndoable; import java.util.ArrayList; import java.util.Collections; -import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -56,34 +56,34 @@ private static final Log log = LogFactory.getLog(ReplicationNode.class); /** contract of entity of the node. */ - final TopiaEntityEnum contract; + protected final TopiaEntityEnum contract; /** entity operator. */ - final EntityOperator<? super TopiaEntity> operator; + protected final EntityOperator<? super TopiaEntity> operator; /** shell of the node. */ - Set<ReplicationNode> shell; + protected Set<ReplicationNode> shell; /** * dictionnary of associations defined on the node (keys are association * name, and values are target node). */ - Map<String, ReplicationNode> associations; + protected final Map<String, ReplicationNode> associations; /** names of association to dettach while replication. */ - Set<String> associationsToDettach; + protected final Set<String> associationsToDettach; /** * compositions defined on the node (keys are association name, * and values are target node). */ - Map<String, ReplicationNode> dependencies; + protected final Map<String, ReplicationNode> dependencies; /** names of dependency to dettach while replication. */ - Set<String> dependenciesToDettach; + protected final Set<String> dependenciesToDettach; /** operations to fire when replication pass on this node. */ - List<ReplicationOperationDef> operations; + protected final List<ReplicationOperationDef> operations; public ReplicationNode(TopiaEntityEnum contract) { this.contract = contract; @@ -117,10 +117,23 @@ } } - public List<ReplicationOperationDef> getOperations() { - return operations; + public ReplicationOperationDef[] getOperations() { + return operations.toArray( + new ReplicationOperationDef[operations.size()]); } + public ReplicationOperationDef[] getUndoableOperations() { + List<ReplicationOperationDef> result = + new ArrayList<ReplicationOperationDef>(); + for (ReplicationOperationDef operation : operations) { + if (TopiaReplicationOperationUndoable.class.isAssignableFrom( + operation.getOperationClass())) { + result.add(operation); + } + } + return result.toArray(new ReplicationOperationDef[result.size()]); + } + public boolean hasAssociation() { return !associations.isEmpty(); } @@ -187,6 +200,10 @@ return contract; } + public Class<? extends TopiaEntity> getEntityType() { + return contract.getContract(); + } + public EntityOperator<? super TopiaEntity> getOperator() { return operator; } @@ -204,15 +221,22 @@ } /** - * Les operations sont triees sur leur phase. + * sort operation by their phase. * * @see ReplicationOperationPhase - * @see ReplicationNode#OPERATION_COMPARATOR */ public void sortOperations() { - Collections.sort(operations, OPERATION_COMPARATOR); + Collections.sort(operations); } + /** + * Remove all operation of the node (for example when no data is associated + * with the type of the node, then no needed operations). + */ + public void clearOperations() { + operations.clear(); + } + @Override public boolean equals(Object obj) { if (obj == null) { @@ -236,20 +260,4 @@ public String toString() { return contract.toString(); } - - /** - * {@link ReplicationOperationDef} comparator based on operation's phase. - * - * @see ReplicationOperationDef#getPhase() - */ - public static final Comparator<ReplicationOperationDef> - OPERATION_COMPARATOR = new Comparator<ReplicationOperationDef>() { - - @Override - public int compare(ReplicationOperationDef o1, - ReplicationOperationDef o2) { - int result = o1.getPhase().compareTo(o2.getPhase()); - return result; - } - }; } Modified: trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/model/ReplicationOperationDef.java =================================================================== --- trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/model/ReplicationOperationDef.java 2010-08-21 16:02:52 UTC (rev 2104) +++ trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/model/ReplicationOperationDef.java 2010-08-21 16:05:12 UTC (rev 2105) @@ -25,6 +25,8 @@ package org.nuiton.topia.replication.model; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.TopiaEntityEnum; import org.nuiton.topia.replication.TopiaReplicationOperation; import java.util.Arrays; @@ -42,21 +44,23 @@ * <p/> * This definition is detected when building replication model. * <p/> - * Then when starting replication, base on this definition, we can build the - * operation to fire. + * Then when starting replication, based on this definition, we can instanciate + * the operation to execute. + * <p/> + * <b>Note:</b> Such objects can be comparable via their {@link #phase}. * * @author tchemit <chemit@codelutin.com> * @since 2.2.0 */ -public class ReplicationOperationDef { +public class ReplicationOperationDef implements Comparable<ReplicationOperationDef> { - final ReplicationOperationPhase phase; + protected final ReplicationOperationPhase phase; - final Class<? extends TopiaReplicationOperation> operationClass; + protected final Class<? extends TopiaReplicationOperation> operationClass; - final ReplicationNode node; + protected final ReplicationNode node; - final Object[] parameters; + protected final Object[] parameters; public ReplicationOperationDef( ReplicationOperationPhase phase, @@ -85,10 +89,44 @@ return phase; } + public TopiaEntityEnum getContract() { + return node.getContract(); + } + + public Class<? extends TopiaEntity> getEntityType() { + return node.getEntityType(); + } + @Override public String toString() { return " <" + operationClass.getSimpleName() + " on " + node + (parameters.length == 0 ? "" : ", params:" + Arrays.toString(parameters)) + ">"; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ReplicationOperationDef that = (ReplicationOperationDef) o; + + if (!node.equals(that.node)) return false; + if (!operationClass.equals(that.operationClass)) return false; + return phase == that.phase; + } + + @Override + public int hashCode() { + int result = phase.hashCode(); + result = 31 * result + operationClass.hashCode(); + result = 31 * result + node.hashCode(); + return result; + } + + @Override + public int compareTo(ReplicationOperationDef o) { + int result = getPhase().compareTo(o.getPhase()); + return result; + } } Modified: trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/operation/AttachAssociation.java =================================================================== --- trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/operation/AttachAssociation.java 2010-08-21 16:02:52 UTC (rev 2104) +++ trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/operation/AttachAssociation.java 2010-08-21 16:05:12 UTC (rev 2105) @@ -32,7 +32,7 @@ import org.nuiton.topia.persistence.TopiaEntity; import org.nuiton.topia.persistence.util.EntityOperator; import org.nuiton.topia.persistence.util.TopiaEntityHelper; -import org.nuiton.topia.persistence.util.TopiaEntityIdsMap; +import org.nuiton.topia.replication.TopiaReplicationContext; import org.nuiton.topia.replication.TopiaReplicationOperation; import org.nuiton.topia.replication.model.ReplicationModel; import org.nuiton.topia.replication.model.ReplicationNode; @@ -48,18 +48,28 @@ /** * Pour attacher une association. * <p/> + * L'opération requière 2 ou 3 paramètres : + * <ul> + * <li>{@code parameters[0]} : le nom de l'association à traiter</li> + * <li>{@code parameters[1]} : un drapeau pour savoir si on est sur le reverse + * de l'association</li> + * <li>{@code parameters[2]} : le noeud source de l'association + * (uniquement utilisé si on est sur le reverse d'une association)</li> + * </ul> + * <p/> * Deux cas peuvent se produire : * <p/> * - le noeud de l'operation est la source de l'association, dans ce cas la - * <code>nodeEntities</code> contient les entites sources de l'association et on + * {@code entities} contient les entites sources de l'association et on * retrouve les entites associes a partir du type de l'association * <p/> - * Ce premier cas est verifie quand reverse (le econd parametre) est a false + * Ce premier cas est verifie quand {@code reverse} (le second parametre) est + * à {@code false}. * <p/> * - le noeud de l'operation est la cible de l'association, dans ce cas la - * <code>nodeEntities</code> contient les entities associes (cibles) de + * {@code entities} contient les entities associées (cibles) de * l'assocation et on retrouve les entities a partir d'un troisieme parametre - * qui donne le node source des entities sources. + * qui donne le node source de l'association. * <p/> * Note : cette operation est interne, et n'est pas creable par l'utilisateur * via la methode {@link #register(ReplicationModel, ReplicationNode, @@ -80,22 +90,22 @@ Object... parameters) { throw new UnsupportedOperationException( - _("topia.replication.operation.error.uncreatable", getClass())); + _("topia.replication.error.operation.uncreatable", getClass())); } @Override - public void run(ReplicationOperationDef operationDef, + public void run(TopiaReplicationContext replicationContext, + ReplicationOperationDef operationDef, TopiaContextImplementor srcCtxt, TopiaContextImplementor dstCtxt, - List<? extends TopiaEntity> nodeEntities, - TopiaEntityIdsMap data) throws TopiaException { + List<? extends TopiaEntity> entities) throws TopiaException { String name = (String) operationDef.getParameters()[0]; Boolean reverse = (Boolean) operationDef.getParameters()[1]; if (log.isDebugEnabled()) { log.debug("currentNode : " + operationDef.getNode() + - " , association name : " + name + " reverse ? " + reverse); + ", association name : " + name + " reverse ? " + reverse); } ReplicationNode ownerNode; @@ -120,8 +130,8 @@ cibleNode = operationDef.getNode(); - ownerIds = TopiaEntityHelper.getTopiaIdList(nodeEntities); - associationsId = data.get(cibleNode.getContract().getContract()); + ownerIds = TopiaEntityHelper.getTopiaIdList(entities); + associationsId = replicationContext.getEntityIds(cibleNode); } else { @@ -135,8 +145,8 @@ cibleNode = ownerNode.getAssociations().get(name); - ownerIds = data.get(ownerOperator.getClazz()); - associationsId = TopiaEntityHelper.getTopiaIdList(nodeEntities); + ownerIds = replicationContext.getEntityIds(cibleNode); + associationsId = TopiaEntityHelper.getTopiaIdList(entities); } if (ownerIds == null || ownerIds.isEmpty()) { @@ -160,7 +170,6 @@ boolean shouldCommit = false; - if (log.isInfoEnabled()) { log.info("ownerNode : " + ownerNode + " , targetNode : " + cibleNode + ", association : " + name); @@ -173,11 +182,13 @@ for (TopiaEntity src : ownerEntities) { // les association cibles connues pour l'entite sur la base source - Collection<?> targetEntities = (Collection<?>) ownerOperator.get(name, src); + Collection<?> targetEntities = + (Collection<?>) ownerOperator.get(name, src); if (targetEntities == null || targetEntities.isEmpty()) { if (log.isDebugEnabled()) { - log.debug("no association '" + name + "' attached to " + src); + log.debug("no association '" + name + "' attached to " + + src); } // pas de donnees dans l'association continue; @@ -191,7 +202,8 @@ TopiaEntity dst = dstCtxt.findByTopiaId(src.getTopiaId()); - // les association cibles connues pour l'entite sur la base destination + // les association cibles connues pour l'entite sur la base + // destination Collection<?> dstTargetEntities = (Collection<?>) ownerOperator.get(name, dst); @@ -209,7 +221,8 @@ // on verifie que l'association doit etre rattachee if (associationsId.contains(assosiationSrc.getTopiaId())) { - if (dstTargetAssociationsId.contains(assosiationSrc.getTopiaId())) { + if (dstTargetAssociationsId.contains( + assosiationSrc.getTopiaId())) { // deja attache if (log.isDebugEnabled()) { log.debug("already attached association '" + name + @@ -249,14 +262,4 @@ dstCtxt.commitTransaction(); } } - - @Override - public void rollback(ReplicationOperationDef operationDef, - TopiaContextImplementor srcCtxt, - TopiaContextImplementor dstCtxt, - List<? extends TopiaEntity> entities, - TopiaEntityIdsMap data) throws Exception { - - // il faut dettacher les associations - } } Modified: trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/operation/AttachLink.java =================================================================== --- trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/operation/AttachLink.java 2010-08-21 16:02:52 UTC (rev 2104) +++ trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/operation/AttachLink.java 2010-08-21 16:05:12 UTC (rev 2105) @@ -32,7 +32,7 @@ import org.nuiton.topia.persistence.TopiaEntity; import org.nuiton.topia.persistence.util.EntityOperator; import org.nuiton.topia.persistence.util.TopiaEntityHelper; -import org.nuiton.topia.persistence.util.TopiaEntityIdsMap; +import org.nuiton.topia.replication.TopiaReplicationContext; import org.nuiton.topia.replication.TopiaReplicationOperation; import org.nuiton.topia.replication.model.ReplicationLink; import org.nuiton.topia.replication.model.ReplicationModel; @@ -81,17 +81,19 @@ Object... parameters) { throw new UnsupportedOperationException( - _("topia.replication.operation.error.uncreatable", getClass())); + _("topia.replication.error.operation.uncreatable", getClass())); } @Override - public void run(ReplicationOperationDef operationDef, + public void run(TopiaReplicationContext replicationContext, + ReplicationOperationDef operationDef, TopiaContextImplementor srcCtxt, TopiaContextImplementor dstCtxt, - List<? extends TopiaEntity> nodeEntities, - TopiaEntityIdsMap data) throws TopiaException { + List<? extends TopiaEntity> nodeEntities + ) throws TopiaException { - ReplicationLink link = (ReplicationLink) operationDef.getParameters()[0]; + ReplicationLink link = + (ReplicationLink) operationDef.getParameters()[0]; String name = link.getName(); @@ -112,9 +114,6 @@ // contient la liste des ids des entite source de l'association List<String> ownerIds = null; - // contient la liste des entites sources de l'association - List<? extends TopiaEntity> ownerEntities = null; - if (ownerNode.equals(operationDef.getNode())) { ownerIds = TopiaEntityHelper.getTopiaIdList(nodeEntities); } @@ -124,11 +123,11 @@ } if (ownerIds == null) { - ownerIds = data.get(ownerNode.getContract().getContract()); + ownerIds = replicationContext.getEntityIds(ownerNode); } if (associationIds == null) { - associationIds = data.get(cibleNode.getContract().getContract()); + associationIds = replicationContext.getEntityIds(cibleNode); } if (ownerIds == null || ownerIds.isEmpty()) { @@ -142,10 +141,12 @@ // modifiees (dettachement d'association ou autres) // ils nous faut les entites telles qu'elles sont en base source - ownerEntities = TopiaEntityHelper.getEntitiesList( - srcCtxt, - ownerIds.toArray(new String[ownerIds.size()]) - ); + // contient la liste des entites sources de l'association + List<? extends TopiaEntity> ownerEntities = + TopiaEntityHelper.getEntitiesList( + srcCtxt, + ownerIds.toArray(new String[ownerIds.size()]) + ); boolean shouldCommit = false; @@ -167,7 +168,8 @@ if (targetEntities == null || targetEntities.isEmpty()) { if (log.isDebugEnabled()) { - log.debug("no association '" + name + "' attached to " + src); + log.debug("no association '" + name + "' attached to " + + src); } // pas de donnees dans l'association continue; @@ -181,7 +183,8 @@ TopiaEntity dst = dstCtxt.findByTopiaId(src.getTopiaId()); - // les association cibles connues pour l'entite sur la base destination + // les association cibles connues pour l'entite sur la base + // destination Collection<?> dstTargetEntities = (Collection<?>) ownerOperator.get(name, dst); @@ -239,14 +242,4 @@ dstCtxt.commitTransaction(); } } - - @Override - public void rollback(ReplicationOperationDef operationDef, - TopiaContextImplementor srcCtxt, - TopiaContextImplementor dstCtxt, - List<? extends TopiaEntity> entities, - TopiaEntityIdsMap data) throws Exception { - - // il faut dettacher les liens - } } Modified: trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/operation/DettachAssociation.java =================================================================== --- trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/operation/DettachAssociation.java 2010-08-21 16:02:52 UTC (rev 2104) +++ trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/operation/DettachAssociation.java 2010-08-21 16:05:12 UTC (rev 2105) @@ -31,7 +31,7 @@ import org.nuiton.topia.framework.TopiaContextImplementor; import org.nuiton.topia.persistence.TopiaEntity; import org.nuiton.topia.persistence.util.EntityOperator; -import org.nuiton.topia.persistence.util.TopiaEntityIdsMap; +import org.nuiton.topia.replication.TopiaReplicationContext; import org.nuiton.topia.replication.TopiaReplicationOperation; import org.nuiton.topia.replication.model.ReplicationModel; import org.nuiton.topia.replication.model.ReplicationNode; @@ -64,20 +64,21 @@ Object... parameters) { throw new UnsupportedOperationException( - _("topia.replication.operation.error.uncreatable", getClass())); + _("topia.replication.error.operation.uncreatable", getClass())); } @Override - public void run(ReplicationOperationDef operationDef, + public void run(TopiaReplicationContext replicationContext, + ReplicationOperationDef operationDef, TopiaContextImplementor srcCtxt, TopiaContextImplementor dstCtxt, - List<? extends TopiaEntity> entities, - TopiaEntityIdsMap data) throws TopiaException { + List<? extends TopiaEntity> entities + ) throws TopiaException { String name = (String) operationDef.getParameters()[0]; - EntityOperator<? super TopiaEntity> operator = - operationDef.getNode().getOperator(); + EntityOperator<? super TopiaEntity> operator; + operator = operationDef.getNode().getOperator(); // dettach les associations for (TopiaEntity e : entities) { @@ -92,15 +93,4 @@ } } } - - - @Override - public void rollback(ReplicationOperationDef operationDef, - TopiaContextImplementor srcCtxt, - TopiaContextImplementor dstCtxt, - List<? extends TopiaEntity> entities, - TopiaEntityIdsMap data) throws Exception { - - // il faut attacher les associations ? ou bien ne rien faire ? - } } Modified: trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/operation/Duplicate.java =================================================================== --- trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/operation/Duplicate.java 2010-08-21 16:02:52 UTC (rev 2104) +++ trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/operation/Duplicate.java 2010-08-21 16:05:12 UTC (rev 2105) @@ -25,11 +25,15 @@ package org.nuiton.topia.replication.operation; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.nuiton.topia.TopiaException; import org.nuiton.topia.framework.TopiaContextImplementor; +import org.nuiton.topia.persistence.TopiaDAO; import org.nuiton.topia.persistence.TopiaEntity; -import org.nuiton.topia.persistence.util.TopiaEntityIdsMap; -import org.nuiton.topia.replication.TopiaReplicationOperation; +import org.nuiton.topia.replication.TopiaReplicationContext; +import org.nuiton.topia.replication.TopiaReplicationOperationUndoable; import org.nuiton.topia.replication.model.ReplicationModel; import org.nuiton.topia.replication.model.ReplicationNode; import org.nuiton.topia.replication.model.ReplicationOperationDef; @@ -49,40 +53,71 @@ * @author tchemit <chemit@codelutin.com> * @since 2.2.0 */ -public class Duplicate implements TopiaReplicationOperation { +public class Duplicate implements TopiaReplicationOperationUndoable { + + /** Logger */ + private static final Log log = + LogFactory.getLog(Duplicate.class); + @Override public void register(ReplicationModel model, ReplicationNode ownerNode, ReplicationOperationPhase phase, - Object... parameters) { + Object... parameters) throws UnsupportedOperationException { throw new UnsupportedOperationException( - _("topia.replication.operation.error.uncreatable", getClass())); + _("topia.replication.error.operation.uncreatable", getClass())); } @Override - public void run(ReplicationOperationDef operationDef, + public void run(TopiaReplicationContext replicationContext, + ReplicationOperationDef operationDef, TopiaContextImplementor srcCtxt, TopiaContextImplementor dstCtxt, - List<? extends TopiaEntity> entities, - TopiaEntityIdsMap data) throws TopiaException { + List<? extends TopiaEntity> entities) throws TopiaException { // replication des donnees vers la destination srcCtxt.replicateEntities(dstCtxt, entities); // sauvegarde de la destination dstCtxt.commitTransaction(); - } @Override public void rollback(ReplicationOperationDef operationDef, - TopiaContextImplementor srcCtxt, - TopiaContextImplementor dstCtxt, - List<? extends TopiaEntity> entities, - TopiaEntityIdsMap data) throws Exception { + TopiaReplicationContext replicationContext, + TopiaContextImplementor dstCtxt) throws Exception { - // il faut supprimer les entities + List<String> ids = + replicationContext.getEntityIds(operationDef.getNode()); + + if (CollectionUtils.isEmpty(ids)) { + + // rien a supprimer + return; + } + + Class<? extends TopiaEntity> entityClass = operationDef.getEntityType(); + TopiaDAO<TopiaEntity> dao = + (TopiaDAO<TopiaEntity>) dstCtxt.getDAO(entityClass); + + List<String> allIds = dao.findAllIds(); + try { + for (String id : ids) { + if (allIds.contains(id)) { + + // on peut supprimer cette entité + log.info("Will delete " + id); + TopiaEntity entity = dao.findByTopiaId(id); + dao.delete(entity); + } + } + } finally { + allIds.clear(); + + // commit des suppressions + dstCtxt.commitTransaction(); + } } } Modified: trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/operation/LoadLink.java =================================================================== --- trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/operation/LoadLink.java 2010-08-21 16:02:52 UTC (rev 2104) +++ trunk/topia-service-replication/src/main/java/org/nuiton/topia/replication/operation/LoadLink.java 2010-08-21 16:05:12 UTC (rev 2105) @@ -31,7 +31,7 @@ import org.nuiton.topia.framework.TopiaContextImplementor; import org.nuiton.topia.persistence.TopiaEntity; import org.nuiton.topia.persistence.util.EntityOperator; -import org.nuiton.topia.persistence.util.TopiaEntityIdsMap; +import org.nuiton.topia.replication.TopiaReplicationContext; import org.nuiton.topia.replication.TopiaReplicationOperation; import org.nuiton.topia.replication.model.ReplicationLink; import org.nuiton.topia.replication.model.ReplicationModel; @@ -65,15 +65,16 @@ Object... parameters) { throw new UnsupportedOperationException( - _("topia.replication.operation.error.uncreatable", getClass())); + _("topia.replication.error.operation.uncreatable", getClass())); } @Override - public void run(ReplicationOperationDef operationDef, + public void run(TopiaReplicationContext replicationContext, + ReplicationOperationDef operationDef, TopiaContextImplementor srcCtxt, TopiaContextImplementor dstCtxt, - List<? extends TopiaEntity> nodeEntities, - TopiaEntityIdsMap data) throws TopiaException { + List<? extends TopiaEntity> nodeEntities + ) throws TopiaException { ReplicationLink link = (ReplicationLink) operationDef.getParameters()[0]; @@ -83,7 +84,7 @@ if (!ownerNode.equals(operationDef.getNode())) { throw new IllegalStateException( - _("topia.replication.operation.loadLink.illegalSource", + _("topia.replication.error.operation.loadLink.illegalSource", operationDef.getNode(), ownerNode)); } @@ -105,15 +106,4 @@ } } } - - - @Override - public void rollback(ReplicationOperationDef operationDef, - TopiaContextImplementor srcCtxt, - TopiaContextImplementor dstCtxt, - List<? extends TopiaEntity> entities, - TopiaEntityIdsMap data) throws Exception { - - // rien a faire ici - } } Modified: trunk/topia-service-replication/src/main/resources/i18n/topia-service-replication-en_GB.properties =================================================================== --- trunk/topia-service-replication/src/main/resources/i18n/topia-service-replication-en_GB.properties 2010-08-21 16:02:52 UTC (rev 2104) +++ trunk/topia-service-replication/src/main/resources/i18n/topia-service-replication-en_GB.properties 2010-08-21 16:05:12 UTC (rev 2105) @@ -1,7 +1,5 @@ -topia.replication.attachAssociation.nothing.to.do= -topia.replication.engine.error.null.param= -topia.replication.engine.error.unkown.operation= -topia.replication.engine.error.unkown.owner.node= -topia.replication.operation.error.notImplemented= -topia.replication.operation.error.uncreatable= -topia.replication.operation.loadLink.illegalSource= +topia.replication.attachAssociation.nothing.to.do=Nothing to attach... +topia.replication.error.operation.loadLink.illegalSource=The source node of loading association requires the node %1$s, but was %2$s. +topia.replication.error.operation.uncreatable=The operation %1$s is internal and can not be added using the api. +topia.replication.error.unkown.operation=The operation %1$s is unknown, known operations are \: %2$s +topia.replication.error.unkown.owner.node=The target node (of type %1$s) for operation %2$s is unknown, known nodes are \: %3$s Modified: trunk/topia-service-replication/src/main/resources/i18n/topia-service-replication-fr_FR.properties =================================================================== --- trunk/topia-service-replication/src/main/resources/i18n/topia-service-replication-fr_FR.properties 2010-08-21 16:02:52 UTC (rev 2104) +++ trunk/topia-service-replication/src/main/resources/i18n/topia-service-replication-fr_FR.properties 2010-08-21 16:05:12 UTC (rev 2105) @@ -1,7 +1,5 @@ topia.replication.attachAssociation.nothing.to.do=Rien a attacher... -topia.replication.engine.error.null.param=Le param\u00E8tre '%1$s' de la methode '%2$s' ne peut pas etre null\! -topia.replication.engine.error.unkown.operation=L'op\u00E9ration %1$s est inconnue, op\u00E9rations connues \: %2$s -topia.replication.engine.error.unkown.owner.node=Le noeud de rattachement (de type %1$s) pour l'op\u00E9ration %2$s n'a pas \u00E9t\u00E9 trouv\u00E9, noeuds connus \: %3$s -topia.replication.operation.error.notImplemented=L'operation %1$s n'est pas encore implant\u00E9e... -topia.replication.operation.error.uncreatable=L'operation %1$s est interne et ne peut pas \u00EAtre cr\u00E9\u00E9e par l'api d'ajout d'op\u00E9ration. -topia.replication.operation.loadLink.illegalSource=Le noeud source de l'op\u00E9ration de chargement d'association attendait un noeud %1$s mais a trouv\u00E9 le noeud %2$s. +topia.replication.error.operation.loadLink.illegalSource=Le noeud source de l'op\u00E9ration de chargement d'association attendait un noeud %1$s mais a trouv\u00E9 le noeud %2$s. +topia.replication.error.operation.uncreatable=L'operation %1$s est interne et ne peut pas \u00EAtre cr\u00E9\u00E9e par l'api d'ajout d'op\u00E9ration. +topia.replication.error.unkown.operation=L'op\u00E9ration %1$s est inconnue, op\u00E9rations connues \: %2$s +topia.replication.error.unkown.owner.node=Le noeud de rattachement (de type %1$s) pour l'op\u00E9ration %2$s n'a pas \u00E9t\u00E9 trouv\u00E9, noeuds connus \: %3$s Modified: trunk/topia-service-replication/src/test/java/org/nuiton/topia/replication/AbstractTopiaReplicationServiceTest.java =================================================================== --- trunk/topia-service-replication/src/test/java/org/nuiton/topia/replication/AbstractTopiaReplicationServiceTest.java 2010-08-21 16:02:52 UTC (rev 2104) +++ trunk/topia-service-replication/src/test/java/org/nuiton/topia/replication/AbstractTopiaReplicationServiceTest.java 2010-08-21 16:05:12 UTC (rev 2105) @@ -122,6 +122,11 @@ service = null; } + + protected TopiaReplicationModelBuilder getModelBuilder() { + return (TopiaReplicationModelBuilder) service.getModelBuilder(); + } + protected abstract TopiaContext createDb2(String name) throws Exception; protected abstract TopiaContext createDb(String name) throws Exception; @@ -215,7 +220,7 @@ Set<?> detectTypes; - detectTypes = service.getModelBuilder().detectTypes(context, getContracts(), entity.getTopiaId()); + detectTypes = ((TopiaReplicationModelBuilder) service.getModelBuilder()).detectTypes(context, getContracts(), entity.getTopiaId()); assertEquals("expected types : " + Arrays.toString(expectedCouple) + " but was " + detectTypes, @@ -226,7 +231,7 @@ } protected void getOperation(Class<? extends TopiaReplicationOperation> operationClass, boolean shouldExist) throws TopiaException { - TopiaReplicationOperation operation = service.getModelBuilder().getOperation(operationClass); + TopiaReplicationOperation operation = getModelBuilder().getOperationProvider().getOperation(operationClass); assertEquals(shouldExist, operation != null); } @@ -280,14 +285,14 @@ TopiaEntityEnum c = TopiaEntityHelper.getEntityEnum( entity.getClass(), getContracts()); assertNotNull(c); - shell = model.getNode(c.getContract()).getShell(); + shell = model.getNode(c).getShell(); assertEquals( "expected shell : " + Arrays.toString(expected) + ", but was " + shell, expected.length, shell.size()); for (int i = 0, j = expected.length; i < j; i++) { TopiaEntityEnum type = expected[i]; - ReplicationNode node = model.getNode(type.getContract()); + ReplicationNode node = model.getNode(type); assertTrue(shell.contains(node)); assertEquals(type, node.getContract()); } @@ -388,7 +393,7 @@ } for (ReplicationNode node : model.getOrder()) { - List<ReplicationOperationDef> operations = node.getOperations(); + ReplicationOperationDef[] operations = node.getOperations(); for (ReplicationOperationDef op : operations) { getLog().info("[" + node + "] : operation " + op); } @@ -399,7 +404,8 @@ private static int dbCounter; - protected void doReplicate(TopiaEntityEnum contract, TopiaEntity... entity) throws Exception { + protected void doReplicate(TopiaEntityEnum contract, + TopiaEntity... entity) throws Exception { TopiaContext rootCtxt = createReplicateDb("doReplicate_" + contract); @@ -596,21 +602,22 @@ getLog().info("entity " + entity.getTopiaId()); prepareModel(entity.getTopiaId()); - service.addBeforeOperation(model, contract, operationClass, parameters); + getModelBuilder().addBeforeOperation(model, contract, operationClass, parameters); // on ne doit pas avoir le droit de creer cette operation fail(); } - protected void createUnsupportedAfterOperation(TopiaEntityEnum contract, - TopiaEntity entity, - Class<? extends TopiaReplicationOperation> operationClass, - Object... parameters) throws Exception { + protected void createUnsupportedAfterOperation( + TopiaEntityEnum contract, + TopiaEntity entity, + Class<? extends TopiaReplicationOperation> operationClass, + Object... parameters) throws Exception { getLog().info("entity " + entity.getTopiaId()); prepareModel(entity.getTopiaId()); // model = service.createModel(getContracts()); // model.detectDirectDependencies(); - service.addAfterOperation(model, contract, operationClass, parameters); + getModelBuilder().addAfterOperation(model, contract, operationClass, parameters); // on ne doit pas avoir le droit de creer cette operation fail(); } @@ -637,11 +644,15 @@ } protected void createModel(TopiaEntity entity) throws TopiaException { - model = service.getModelBuilder().createModel(context, getContracts(), entity.getTopiaId()); + model = getModelBuilder().createModel(context, + getContracts(), + true, + entity.getTopiaId() + ); } protected void prepareModel(String... ids) throws TopiaException { - model = service.prepare(getContracts(), ids); + model = service.prepare(getContracts(), true, ids); } protected void prepareModelAll() throws TopiaException { @@ -649,6 +660,6 @@ } protected void prepareModelWithComputedOrder(String... ids) throws TopiaException { - model = service.prepareWithComputedOrder(getContracts(), ids); + model = service.prepare(getContracts(), false, ids); } } Modified: trunk/topia-service-replication/src/test/java/org/nuiton/topia/replication/TopiaReplicationOperationTest.java =================================================================== --- trunk/topia-service-replication/src/test/java/org/nuiton/topia/replication/TopiaReplicationOperationTest.java 2010-08-21 16:02:52 UTC (rev 2104) +++ trunk/topia-service-replication/src/test/java/org/nuiton/topia/replication/TopiaReplicationOperationTest.java 2010-08-21 16:05:12 UTC (rev 2105) @@ -67,9 +67,16 @@ private static final Log log = LogFactory.getLog(TopiaReplicationOperationTest.class); - protected static final TopiaEntityEnum[] contracts = {TopiaTestEntityEnum.Person, TopiaTestEntityEnum.Pet, TopiaTestEntityEnum.Race}; + protected static final TopiaEntityEnum[] contracts = { + TopiaTestEntityEnum.Person, + TopiaTestEntityEnum.Pet, + TopiaTestEntityEnum.Race + }; - protected static final String entitiesList = PersonImpl.class.getName() + "," + PetImpl.class.getName() + "," + RaceImpl.class.getName(); + protected static final String entitiesList = + PersonImpl.class.getName() + "," + + PetImpl.class.getName() + "," + + RaceImpl.class.getName(); static protected Person person, person2; @@ -107,7 +114,7 @@ @Test(expected = NullPointerException.class) public void testGetOperation_nullOperationClass() throws Exception { - getModelBuilder().getOperation(null); + getModelBuilder().getOperationProvider().getOperation((Class<? extends TopiaReplicationOperation>) null); } protected TopiaReplicationModelBuilder getModelBuilder() { @@ -134,38 +141,38 @@ @Test(expected = NullPointerException.class) public void testCreateOperation_nullType() throws Exception { - model = getModelBuilder().createModel(context, contracts); + model = getModelBuilder().createModel(context, contracts, true); getModelBuilder().createOperation(model, null, null, null); } @Test(expected = NullPointerException.class) public void testCreateOperation_nullPhase() throws Exception { - model = getModelBuilder().createModel(context, contracts); + model = getModelBuilder().createModel(context, contracts, true); getModelBuilder().createOperation(model, TopiaTestEntityEnum.Pet, null, null); } @Test(expected = NullPointerException.class) public void testCreateOperation_nullOperationClass() throws Exception { - model = getModelBuilder().createModel(context, contracts); + model = getModelBuilder().createModel(context, contracts, true); getModelBuilder().createOperation(model, TopiaTestEntityEnum.Pet, ReplicationOperationPhase.before, null); } @Test(expected = IllegalArgumentException.class) public void testCreateOperation_noNode() throws Exception { - model = getModelBuilder().createModel(context, contracts); + model = getModelBuilder().createModel(context, contracts, true); // le noeud Pet n'existe pas - service.addAfterOperation(model, TopiaTestEntityEnum.Pet, Duplicate.class); + getModelBuilder().addAfterOperation(model, TopiaTestEntityEnum.Pet, Duplicate.class); } @Test(expected = IllegalArgumentException.class) public void testCreateOperation_noOperation() throws Exception { - model = getModelBuilder().createModel(context, contracts); + model = getModelBuilder().createModel(context, contracts, true); // le noeud Pet n'existe pas - service.addAfterOperation(model, TopiaTestEntityEnum.Pet, UnregistredOperation.class); + getModelBuilder().addAfterOperation(model, TopiaTestEntityEnum.Pet, UnregistredOperation.class); } @Test(expected = UnsupportedOperationException.class) @@ -211,37 +218,37 @@ @Test(expected = IllegalArgumentException.class) public void testCreateOperation_wrongParameterNumber() throws Exception { - model = getModelBuilder().createModel(context, contracts, pet.getTopiaId()); - service.addBeforeOperation(model, TopiaTestEntityEnum.Pet, FakeOperation.class); + model = getModelBuilder().createModel(context, contracts, true, pet.getTopiaId()); + getModelBuilder().addBeforeOperation(model, TopiaTestEntityEnum.Pet, FakeOperation.class); } @Test(expected = IllegalArgumentException.class) public void testCreateOperation_wrongParameterNumber2() throws Exception { - model = getModelBuilder().createModel(context, contracts, pet.getTopiaId()); - service.addBeforeOperation(model, TopiaTestEntityEnum.Pet, FakeOperation.class, String.class, String.class); + model = getModelBuilder().createModel(context, contracts, true, pet.getTopiaId()); + getModelBuilder().addBeforeOperation(model, TopiaTestEntityEnum.Pet, FakeOperation.class, String.class, String.class); } @Test(expected = IllegalArgumentException.class) public void testCreateOperation_wrongParameterType() throws Exception { - model = getModelBuilder().createModel(context, contracts, pet.getTopiaId()); - service.addBeforeOperation(model, TopiaTestEntityEnum.Pet, FakeOperation.class, Integer.class); + model = getModelBuilder().createModel(context, contracts, true, pet.getTopiaId()); + getModelBuilder().addBeforeOperation(model, TopiaTestEntityEnum.Pet, FakeOperation.class, Integer.class); } @Test(expected = IllegalArgumentException.class) public void testCreateOperation_nullParameter() throws Exception { - model = getModelBuilder().createModel(context, contracts, pet.getTopiaId()); - service.addBeforeOperation(model, TopiaTestEntityEnum.Pet, FakeOperation.class, (Object) null); + model = getModelBuilder().createModel(context, contracts, true, pet.getTopiaId()); + getModelBuilder().addBeforeOperation(model, TopiaTestEntityEnum.Pet, FakeOperation.class, (Object) null); } @Test public void testCreateOperation() throws Exception { - model = getModelBuilder().createModel(context, contracts, pet.getTopiaId()); - service.addBeforeOperation(model, TopiaTestEntityEnum.Pet, FakeOperation.class, "before"); - service.addAfterOperation(model, TopiaTestEntityEnum.Race, FakeOperation.class, "after"); + model = getModelBuilder().createModel(context, contracts, true, pet.getTopiaId()); + getModelBuilder().addBeforeOperation(model, TopiaTestEntityEnum.Pet, FakeOperation.class, "before"); + getModelBuilder().addAfterOperation(model, TopiaTestEntityEnum.Race, FakeOperation.class, "after"); } @Test(expected = NullPointerException.class) @@ -253,7 +260,7 @@ @Test(expected = NullPointerException.class) public void testDoReplicate_nullDstCtxt() throws Exception { - model = getModelBuilder().createModel(context, contracts); + model = getModelBuilder().createModel(context, contracts, true); service.doReplicate(model, null); } Modified: trunk/topia-service-replication/src/test/java/org/nuiton/topia/replication/TopiaReplicationServiceImplAllTest.java =================================================================== --- trunk/topia-service-replication/src/test/java/org/nuiton/topia/replication/TopiaReplicationServiceImplAllTest.java 2010-08-21 16:02:52 UTC (rev 2104) +++ trunk/topia-service-replication/src/test/java/org/nuiton/topia/replication/TopiaReplicationServiceImplAllTest.java 2010-08-21 16:05:12 UTC (rev 2105) @@ -283,7 +283,7 @@ @Override protected void createModel(TopiaEntity entity) throws TopiaException { - model = service.getModelBuilder().createModelForAll(getContracts()); + model = getModelBuilder().createModelForAll(getContracts()); } @Override Modified: trunk/topia-service-replication/src/test/java/org/nuiton/topia/replication/TopiaReplicationServiceImplTest.java =================================================================== --- trunk/topia-service-replication/src/test/java/org/nuiton/topia/replication/TopiaReplicationServiceImplTest.java 2010-08-21 16:02:52 UTC (rev 2104) +++ trunk/topia-service-replication/src/test/java/org/nuiton/topia/replication/TopiaReplicationServiceImplTest.java 2010-08-21 16:05:12 UTC (rev 2105) @@ -66,7 +66,8 @@ public class TopiaReplicationServiceImplTest extends AbstractTopiaReplicationServiceTest { /** Logger */ - private static final Log log = LogFactory.getLog(TopiaReplicationServiceImplTest.class); + private static final Log log = + LogFactory.getLog(TopiaReplicationServiceImplTest.class); protected static final TopiaEntityEnum[] contracts = {TopiaTestEntityEnum.Person, TopiaTestEntityEnum.Pet, TopiaTestEntityEnum.Race}; @@ -146,7 +147,6 @@ detectAssociations(person2); detectAssociations(race2); detectAssociations(pet2); - } @Test Modified: trunk/topia-service-replication/src/test/java/org/nuiton/topia/replication/operation/FakeOperation.java =================================================================== --- trunk/topia-service-replication/src/test/java/org/nuiton/topia/replication/operation/FakeOperation.java 2010-08-21 16:02:52 UTC (rev 2104) +++ trunk/topia-service-replication/src/test/java/org/nuiton/topia/replication/operation/FakeOperation.java 2010-08-21 16:05:12 UTC (rev 2105) @@ -25,13 +25,11 @@ package org.nuiton.topia.replication.operation; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.nuiton.topia.TopiaException; import org.nuiton.topia.framework.TopiaContextImplementor; import org.nuiton.topia.persistence.TopiaEntity; import org.nuiton.topia.persistence.util.TopiaEntityHelper; -import org.nuiton.topia.persistence.util.TopiaEntityIdsMap; +import org.nuiton.topia.replication.TopiaReplicationContext; import org.nuiton.topia.replication.TopiaReplicationOperation; import org.nuiton.topia.replication.model.ReplicationModel; import org.nuiton.topia.replication.model.ReplicationNode; @@ -46,11 +44,9 @@ */ public class FakeOperation implements TopiaReplicationOperation { - /** Logger */ - private static final Log log = LogFactory.getLog(FakeOperation.class); + public static final Class<?>[] PARAMETERS_CLASSES = + new Class<?>[]{String.class}; - public static final Class<?>[] PARAMETERS_CLASSES = new Class<?>[]{String.class}; - @Override public void register(ReplicationModel model, ReplicationNode ownerNode, @@ -68,17 +64,14 @@ } @Override - public void run(ReplicationOperationDef operationDef, + public void run(TopiaReplicationContext replicationContext, + ReplicationOperationDef operationDef, TopiaContextImplementor srcCtxt, TopiaContextImplementor dstCtxt, - List<? extends TopiaEntity> nodeEntities, - TopiaEntityIdsMap data) + List<? extends TopiaEntity> nodeEntities + ) throws TopiaException { } - @Override - public void rollback(ReplicationOperationDef operationDef, TopiaContextImplementor srcCtxt, TopiaContextImplementor dstCtxt, List<? extends TopiaEntity> entities, TopiaEntityIdsMap data) throws Exception { - // rien a faire - } } Modified: trunk/topia-service-replication/src/test/java/org/nuiton/topia/replication/operation/UncreatableOperation.java =================================================================== --- trunk/topia-service-replication/src/test/java/org/nuiton/topia/replication/operation/UncreatableOperation.java 2010-08-21 16:02:52 UTC (rev 2104) +++ trunk/topia-service-replication/src/test/java/org/nuiton/topia/replication/operation/UncreatableOperation.java 2010-08-21 16:05:12 UTC (rev 2105) @@ -25,12 +25,10 @@ package org.nuiton.topia.replication.operation; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.nuiton.topia.TopiaException; import org.nuiton.topia.framework.TopiaContextImplementor; import org.nuiton.topia.persistence.TopiaEntity; -import org.nuiton.topia.persistence.util.TopiaEntityIdsMap; +import org.nuiton.topia.replication.TopiaReplicationContext; import org.nuiton.topia.replication.TopiaReplicationOperation; import org.nuiton.topia.replication.model.ReplicationModel; import org.nuiton.topia.replication.model.ReplicationNode; @@ -47,10 +45,6 @@ */ public class UncreatableOperation implements TopiaReplicationOperation { - /** Logger */ - private static final Log log = - LogFactory.getLog(UncreatableOperation.class); - @Override public void register(ReplicationModel model, ReplicationNode ownerNode, @@ -58,20 +52,17 @@ Object... parameters) { throw new UnsupportedOperationException( - _("topia.replication.operation.error.uncreatable", getClass())); + _("topia.replication.error.operation.uncreatable", getClass())); } @Override - public void run(ReplicationOperationDef operationDef, + public void run(TopiaReplicationContext replicationContext, + ReplicationOperationDef operationDef, TopiaContextImplementor srcCtxt, TopiaContextImplementor dstCtxt, - List<? extends TopiaEntity> nodeEntities, - TopiaEntityIdsMap data) + List<? extends TopiaEntity> nodeEntities + ) throws TopiaException { } - @Override - public void rollback(ReplicationOperationDef operationDef, TopiaContextImplementor srcCtxt, TopiaContextImplementor dstCtxt, List<? extends TopiaEntity> entities, TopiaEntityIdsMap data) throws Exception { - // rien a faire - } } Modified: trunk/topia-service-replication/src/test/java/org/nuiton/topia/replication/operation/UnregistredOperation.java =================================================================== --- trunk/topia-service-replication/src/test/java/org/nuiton/topia/replication/operation/UnregistredOperation.java 2010-08-21 16:02:52 UTC (rev 2104) +++ trunk/topia-service-replication/src/test/java/org/nuiton/topia/replication/operation/UnregistredOperation.java 2010-08-21 16:05:12 UTC (rev 2105) @@ -25,12 +25,10 @@ package org.nuiton.topia.replication.operation; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.nuiton.topia.TopiaException; import org.nuiton.topia.framework.TopiaContextImplementor; import org.nuiton.topia.persistence.TopiaEntity; -import org.nuiton.topia.persistence.util.TopiaEntityIdsMap; +import org.nuiton.topia.replication.TopiaReplicationContext; import org.nuiton.topia.replication.TopiaReplicationOperation; import org.nuiton.topia.replication.model.ReplicationModel; import org.nuiton.topia.replication.model.ReplicationNode; @@ -45,9 +43,6 @@ */ public class UnregistredOperation implements TopiaReplicationOperation { - /** Logger */ - private static final Log log = LogFactory.getLog(UnregistredOperation.class); - @Override public void register(ReplicationModel model, ReplicationNode ownerNode, @@ -58,16 +53,13 @@ @Override public void run( + TopiaReplicationContext replicationContext, ReplicationOperationDef operationDef, TopiaContextImplementor srcCtxt, TopiaContextImplementor dstCtxt, - List<? extends TopiaEntity> nodeEntities, - TopiaEntityIdsMap data) + List<? extends TopiaEntity> nodeEntities + ) throws TopiaException { } - @Override - public void rollback(ReplicationOperationDef operationDef, TopiaContextImplementor srcCtxt, TopiaContextImplementor dstCtxt, List<? extends TopiaEntity> entities, TopiaEntityIdsMap data) throws Exception { - // rien a faire - } }