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

org.nuiton.topia.templates.sql.order.ReplicationOrderBuilderWithEntryPoint Maven / Gradle / Ivy

package org.nuiton.topia.templates.sql.order;

/*-
 * #%L
 * Toolkit :: Templates
 * %%
 * Copyright (C) 2017 - 2024 Ultreia.io
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU 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 Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program.  If not, see
 * .
 * #L%
 */

import com.google.common.collect.ArrayListMultimap;
import org.apache.commons.lang3.mutable.MutableInt;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.nuiton.topia.service.sql.metadata.TopiaMetadataEntity;
import org.nuiton.topia.service.sql.metadata.TopiaMetadataEntityPath;
import org.nuiton.topia.service.sql.metadata.TopiaMetadataLink;
import org.nuiton.topia.service.sql.metadata.TopiaMetadataModel;
import org.nuiton.topia.service.sql.metadata.TopiaMetadataModelPaths;
import org.nuiton.topia.service.sql.metadata.TopiaMetadataReverseAssociation;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.OptionalInt;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * To compute replication order for a set of entities coming from a single entry point.
 * 

* Created on 24/09/2020. * * @author Tony Chemit - [email protected] * @since 1.27 */ public class ReplicationOrderBuilderWithEntryPoint extends ReplicationOrderBuilder { private static final Logger log = LogManager.getLogger(ReplicationOrderBuilderWithEntryPoint.class); /** * Paths for each entity in this model. */ private final TopiaMetadataModelPaths paths; public static List build(TopiaMetadataModel metadataModel, TopiaMetadataModelPaths paths, TopiaMetadataEntity entryPoint) { Set set = new LinkedHashSet<>(); set.add(Objects.requireNonNull(entryPoint)); set.addAll(Objects.requireNonNull(paths).keySet()); return new ReplicationOrderBuilderWithEntryPoint(Objects.requireNonNull(metadataModel), Objects.requireNonNull(paths), set).build(); } protected ReplicationOrderBuilderWithEntryPoint(TopiaMetadataModel metadataModel, TopiaMetadataModelPaths paths, Set set) { super(metadataModel, set); this.paths = Objects.requireNonNull(paths); } @Override public List build() { Set entities = getEntities(); List result = new ArrayList<>(entities.size()); ArrayListMultimap pathsMapping = computeReducedPathsMapping(paths, entities); ArrayListMultimap dependenciesMapping = computeDependencies(entities); ArrayListMultimap pathsCount = computePathsCount(pathsMapping); int maxDepth = 0; OptionalInt max = pathsCount.keySet().stream().mapToInt(MutableInt::intValue).max(); if (max.isPresent()) { maxDepth = max.getAsInt(); } log.info(String.format("Found %d max depth to process", maxDepth)); // get optimized order from paths for (int i = 0; i <= maxDepth; i++) { List entitiesForThisRound = pathsCount.get(new MutableInt(i)); if (entitiesForThisRound != null) { addRound(i, entitiesForThisRound, pathsMapping, result); } } // fix order for some entities with skipped relation for paths (to avoid cycle) int fixRound = 0; while (fixDependenciesOrder(fixRound, dependenciesMapping, result) > 0) { fixRound++; if (fixRound == 100000) { log.error(String.format("Abord after %d rounds, :(((", fixRound)); } } return Collections.unmodifiableList(result); } private int fixDependenciesOrder(int round, ArrayListMultimap dependenciesMapping, List result) { log.debug(String.format("Next fix round %d", round)); int fixCount = 0; for (Map.Entry> entry : dependenciesMapping.asMap().entrySet()) { TopiaMetadataEntity entity = entry.getKey(); Collection dependencies = entry.getValue(); if (dependencies != null) { int entityIndex = result.indexOf(entity); for (TopiaMetadataEntity dependency : dependencies) { int dependencyIndex = result.indexOf(dependency); if (dependencyIndex > entityIndex) { // place dependency just before entity log.info(String.format("Place dependency %s (%d) before his owner %s (%d)", dependency.getFullyQualifiedName(), dependencyIndex, entity.getFullyQualifiedName(), entityIndex)); result.remove(dependency); result.add(entityIndex, dependency); entityIndex++; fixCount++; } } } } if (fixCount > 0) { log.info(String.format("For round %d, found %d fixes to apply on dependencies order", round, fixCount)); } return fixCount; } private void addRound(int roundIndex, List entitiesForThisRound, ArrayListMultimap pathsMapping, List result) { for (TopiaMetadataEntity entity : TopiaMetadataEntity.sortByFqn(entitiesForThisRound)) { List paths = pathsMapping.get(entity).stream().filter(p -> p.getLinksSize() == roundIndex).collect(Collectors.toList()); log.info(String.format("[%d] Adding entity %s", roundIndex, entity.getFullyQualifiedName())); for (TopiaMetadataEntityPath path : paths.stream().sorted(Comparator.comparing(p -> p.getEnd().getFullyQualifiedName(), String::compareTo)).collect(Collectors.toList())) { log.info(String.format("[%d] → Adding path %s → %s", roundIndex, path.getStart(), path.getEnd())); for (TopiaMetadataLink link : path) { log.info(String.format("[%d] → Adding link %s", roundIndex, link)); TopiaMetadataEntity owner = link.getOwner(); TopiaMetadataEntity target = link.getTarget(); int indexOfOwner = result.indexOf(owner); int indexOfTarget = result.indexOf(target); if (indexOfOwner == -1) { result.add(owner); indexOfOwner = result.indexOf(owner); } if (link instanceof TopiaMetadataReverseAssociation) { // must set target before owner if (indexOfTarget > -1) { if (indexOfTarget < indexOfOwner) { // already in good position continue; } // remove target result.remove(target); } // insert it just before owner result.add(indexOfOwner, target); } else { if (indexOfTarget == -1) { // add target just at the end result.add(target); } } } } } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy