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

ru.taskurotta.service.dependency.links.Graph Maven / Gradle / Ivy

package ru.taskurotta.service.dependency.links;

import com.fasterxml.jackson.annotation.JsonIgnore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serializable;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

/**
 * This is not thread safe object. It should be synchronized with service by version value.
 * 

* User: romario * Date: 4/5/13 * Time: 11:35 AM */ @SuppressWarnings("UnusedDeclaration") public class Graph implements Serializable { private final static Logger logger = LoggerFactory.getLogger(Graph.class); public final static UUID[] EMPTY_ARRAY = new UUID[0]; private int version; private UUID graphId; //should be equal to process ID /** * Map of all not finished items in this process and its time of start in milliseconds. * It has 0 value if item is not started yet. */ private Map notFinishedItems; /** * Links map where keys are tasks which depends from value set of other tasks. * For example, A(B, C) - A is a key and {B, C} is a set value of map. */ private Map> links; private Set finishedItems; // modification stuff. private Modification modification; private UUID[] readyItems; private long touchTimeMillis; private long lastApplyTimeMillis; /** * generic constructor for deserializer */ public Graph() { this.touchTimeMillis = System.currentTimeMillis(); this.version = 0; this.notFinishedItems = new HashMap<>(); this.links = new HashMap<>(); this.finishedItems = new HashSet<>(); } /** * smart constructor for deserializer */ public Graph(int version, UUID graphId, Map notFinishedItems, Map> links, Set finishedItems, long lastApplyTimeMillis, long touchTimeMillis) { this.version = version; this.graphId = graphId; if (notFinishedItems != null) { this.notFinishedItems = notFinishedItems; } else { this.notFinishedItems = new HashMap<>(); } if (links != null) { this.links = links; } else { this.links = new HashMap<>(); } if (finishedItems != null) { this.finishedItems = finishedItems; } else { this.finishedItems = new HashSet<>(); } this.lastApplyTimeMillis = lastApplyTimeMillis; this.touchTimeMillis = touchTimeMillis; } /** * Create new graph * * @param graphId - should be equal to process ID * @param startItem - ID of the first task in process */ public Graph(UUID graphId, UUID startItem) { this(); this.graphId = graphId; notFinishedItems.put(startItem, 0L); } private static Map> reverseIt(Map> links) { Map> reverseResult = new HashMap<>(); if (links.isEmpty()) { return reverseResult; } for (Map.Entry> entry : links.entrySet()) { for (UUID toItem : entry.getValue()) { Set fromItems = reverseResult.get(toItem); if (fromItems == null) { fromItems = new HashSet<>(); reverseResult.put(toItem, fromItems); } fromItems.add(entry.getKey()); } } return reverseResult; } public int getVersion() { return version; } @JsonIgnore public Modification getModification() { return modification; } public boolean hasNotFinishedItem(UUID itemId) { return notFinishedItems.containsKey(itemId); } public Map getNotFinishedItems() { return notFinishedItems; } public Map> getLinks() { return links; } public void setVersion(int version) { this.version = version; } public void setNotFinishedItems(Map notFinishedItems) { this.notFinishedItems = notFinishedItems; } public void setLinks(Map> links) { this.links = links; } @JsonIgnore public UUID[] getReadyItems() { return readyItems; } public UUID getGraphId() { return graphId; } public void setGraphId(UUID graphId) { this.graphId = graphId; } @JsonIgnore public boolean isFinished() { return notFinishedItems.isEmpty(); } public Set getFinishedItems() { return finishedItems; } public void setFinishedItems(Set finishedItems) { this.finishedItems = finishedItems; } /** * Apply changes to the graph * * @param modification - diff object to apply */ public void apply(Modification modification) { long currentTime = System.currentTimeMillis(); logger.debug("apply() modification = [{}]", modification); this.modification = modification; version++; // process finished item UUID finishedItem = modification.getCompletedItem(); notFinishedItems.remove(finishedItem); finishedItems.add(finishedItem); // get new items without dependencies List readyItemsList = applyNewItems(); // update links collection and get release candidates Set reverseItemLinks = updateLinks(); // update readyItemList with old items that become ready findReadyItems(readyItemsList, reverseItemLinks); // return empty or full array of new ready items. readyItems = readyItemsList.toArray(new UUID[readyItemsList.size()]); for (UUID readyItemsId : readyItems) { notFinishedItems.put(readyItemsId, currentTime); } touchTimeMillis = lastApplyTimeMillis = currentTime; } public Map getAllReadyItems() { Map readyItems = null; for (UUID itemId : notFinishedItems.keySet()) { if (links.get(itemId) != null) { continue; } if (readyItems == null) { readyItems = new HashMap<>(); } readyItems.put(itemId, notFinishedItems.get(itemId)); } return readyItems; } private List applyNewItems() { List readyItemsList = new LinkedList<>(); Collection newItems = modification.getNewItems(); if (newItems != null) { // add all new items to set for (UUID newItemId : newItems) { notFinishedItems.put(newItemId, 0L); } // add all new items without links to readyItemsList Map> newLinks = modification.getLinks(); for (UUID newItem : newItems) { if (newLinks == null) { logger.debug("apply() new item [{}] has no links and added to readyItemsList [{}]", newItem, readyItemsList); readyItemsList.add(newItem); continue; } Set itemDependencies = newLinks.get(newItem); if (itemDependencies == null) { logger.debug("apply() new item [{}] has no links and added to readyItemsList [{}]", newItem, readyItemsList); readyItemsList.add(newItem); continue; } // may be some promises already resolved? boolean isReady = true; for (UUID promiseItem : itemDependencies) { if (newItems.contains(promiseItem) || notFinishedItems.containsKey(promiseItem)) { isReady = false; break; } } if (isReady) { readyItemsList.add(newItem); } } } return readyItemsList; } /** * add all new links to "links" map. update reverseLinks * * @return set of items dependent from finished one */ private Set updateLinks() { // update reverse map with new links Map> reverseLinks = reverseIt(links); Map> newLinks = modification.getLinks(); if (newLinks != null) { for (Map.Entry> entry : newLinks.entrySet()) { Set newItemLinks = entry.getValue(); for (UUID newItemLink : newItemLinks) { // prevent link to already finished item. // it is possible case for @NoWait Promise which are used on deep child task if (!notFinishedItems.containsKey(newItemLink)) { continue; } Set itemLinks = links.get(entry.getKey()); if (itemLinks == null) { itemLinks = new HashSet<>(); links.put(entry.getKey(), itemLinks); } itemLinks.add(newItemLink); } // update reverse map for (UUID thatItem : newItemLinks) { Set reverseItemLinks = getOrCreateReverseItemLinks(reverseLinks, thatItem); reverseItemLinks.add(entry.getKey()); } } } return reverseLinks.get(modification.getCompletedItem()); } /** * remove finished item from all set. * find items without dependencies. * * @param readyItemsList - collection for ready items found * @param reverseItemLinks - collection of release candidates */ private void findReadyItems(List readyItemsList, Set reverseItemLinks) { if (reverseItemLinks == null) { return; } UUID finishedItem = modification.getCompletedItem(); for (UUID releaseCandidate : reverseItemLinks) { Set candidateLinks = links.get(releaseCandidate); candidateLinks.remove(finishedItem); UUID dependencySubstitution = modification.getWaitForAfterRelease(); // update changed dependency if (dependencySubstitution != null) { candidateLinks.add(dependencySubstitution); } else { if (candidateLinks.isEmpty()) { // GC items without dependencies links.remove(releaseCandidate); logger.debug("apply() after remove [{}], item [{}] has no dependencies and added to" + " readyItemsList [{}]", finishedItem, releaseCandidate, readyItemsList); readyItemsList.add(releaseCandidate); } } } } private static Set getOrCreateReverseItemLinks(Map> reverseLinks, UUID item) { Set reverseItemLinks = reverseLinks.get(item); if (reverseItemLinks != null) { return reverseItemLinks; } reverseItemLinks = new HashSet<>(); reverseLinks.put(item, reverseItemLinks); return reverseItemLinks; } public boolean isTaskWaitOtherTasks(UUID taskId, int taskQuantity) { Set waitForTasks = links.get(taskId); logger.debug("waitForTasks = " + waitForTasks); if (waitForTasks == null) { return false; } //noinspection SimplifiableIfStatement if (taskQuantity == -1 && !waitForTasks.isEmpty()) { return true; } return waitForTasks.size() == taskQuantity; } public void clearFinishedItems() { finishedItems.clear(); } /** * Method for creating copy of the graph * * @return copy of the current graph */ public Graph copy() { final Graph copy = new Graph(); copy.setGraphId(graphId); copy.setLinks(copyMapUuidWithSetOfUuid(links)); copy.setNotFinishedItems(new HashMap<>(notFinishedItems)); copy.setFinishedItems(new HashSet<>(finishedItems)); copy.setVersion(version); copy.setTouchTimeMillis(touchTimeMillis); copy.setLastApplyTimeMillis(lastApplyTimeMillis); return copy; } private static Map> copyMapUuidWithSetOfUuid(Map> original) { final Map> copy = new HashMap<>(); for (Map.Entry> entry : original.entrySet()) { Set copyOfSet = new HashSet<>(entry.getValue()); copy.put(entry.getKey(), copyOfSet); } return copy; } @Override public String toString() { return "Graph{" + "version=" + version + ", graphId=" + graphId + ", notFinishedItems=" + notFinishedItems + ", links=" + links + ", finishedItems=" + finishedItems + ", modification=" + modification + ", readyItems=" + Arrays.toString(readyItems) + ", touchTimeMillis=" + touchTimeMillis + ", lastApplyTimeMillis=" + lastApplyTimeMillis + '}'; } public Collection getProcessTasks() { Collection allProcessTasks = new HashSet<>(); allProcessTasks.addAll(notFinishedItems.keySet()); allProcessTasks.addAll(finishedItems); return allProcessTasks; } public long getTouchTimeMillis() { return touchTimeMillis; } public void setTouchTimeMillis(long touchTimeMillis) { this.touchTimeMillis = touchTimeMillis; } public long getLastApplyTimeMillis() { return lastApplyTimeMillis; } public void setLastApplyTimeMillis(long lastApplyTimeMillis) { this.lastApplyTimeMillis = lastApplyTimeMillis; } @SuppressWarnings("RedundantIfStatement") @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Graph)) return false; Graph graph = (Graph) o; if (lastApplyTimeMillis != graph.lastApplyTimeMillis) return false; if (touchTimeMillis != graph.touchTimeMillis) return false; if (version != graph.version) return false; if (finishedItems != null ? !finishedItems.equals(graph.finishedItems) : graph.finishedItems != null) { return false; } if (graphId != null ? !graphId.equals(graph.graphId) : graph.graphId != null) return false; if (links != null ? !links.equals(graph.links) : graph.links != null) return false; if (notFinishedItems != null ? !notFinishedItems.equals(graph.notFinishedItems) : graph.notFinishedItems != null) { return false; } return true; } @Override public int hashCode() { int result = version; result = 31 * result + (graphId != null ? graphId.hashCode() : 0); result = 31 * result + (notFinishedItems != null ? notFinishedItems.hashCode() : 0); result = 31 * result + (links != null ? links.hashCode() : 0); result = 31 * result + (finishedItems != null ? finishedItems.hashCode() : 0); result = 31 * result + (int) (touchTimeMillis ^ (touchTimeMillis >>> 32)); result = 31 * result + (int) (lastApplyTimeMillis ^ (lastApplyTimeMillis >>> 32)); return result; } public void removeModification() { modification = null; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy