forked from Azure/azure-sdk-for-java
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Framework for Creatable composition, managing dependency between them…
… and creating resources based on topotogical order
- Loading branch information
Showing
8 changed files
with
670 additions
and
0 deletions.
There are no files selected for viewing
84 changes: 84 additions & 0 deletions
84
azure-client-runtime/src/main/java/com/microsoft/azure/DAGNode.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package com.microsoft.azure; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
/** | ||
* The type representing node in a {@link DAGraph}. | ||
* | ||
* @param <T> the type of the data stored in the node | ||
*/ | ||
public class DAGNode<T> extends Node<T> { | ||
private List<String> dependentKeys; | ||
private int toBeResolved; | ||
|
||
/** | ||
* Creates a DAG node. | ||
* | ||
* @param key unique id of the node | ||
* @param data data to be stored in the node | ||
*/ | ||
public DAGNode(String key, T data) { | ||
super(key, data); | ||
dependentKeys = new ArrayList<>(); | ||
} | ||
|
||
/** | ||
* @return a list of keys of nodes in {@link DAGraph} those are dependents on this node | ||
*/ | ||
List<String> dependentKeys() { | ||
return this.dependentKeys; | ||
} | ||
|
||
/** | ||
* mark the node identified by the given key as dependent of this node. | ||
* | ||
* @param key the id of the dependent node | ||
*/ | ||
public void addDependent(String key) { | ||
this.dependentKeys.add(key); | ||
} | ||
|
||
/** | ||
* @return a list of keys of nodes in {@link DAGraph} that this node depends on | ||
*/ | ||
public List<String> dependencyKeys() { | ||
return this.children(); | ||
} | ||
|
||
/** | ||
* mark the node identified by the given key as this node's dependency. | ||
* | ||
* @param dependencyKey the id of the dependency node | ||
*/ | ||
public void addDependency(String dependencyKey) { | ||
toBeResolved++; | ||
super.addChild(dependencyKey); | ||
} | ||
|
||
/** | ||
* @return <tt>true</tt> if this node has any dependency | ||
*/ | ||
public boolean hasDependencies() { | ||
return this.hasChildren(); | ||
} | ||
|
||
/** | ||
* @return <tt>true</tt> if all dependencies of this node are ready to be consumed | ||
*/ | ||
boolean hasAllResolved() { | ||
return toBeResolved == 0; | ||
} | ||
|
||
/** | ||
* Reports that one of this node's dependency has been resolved and ready to be consumed. | ||
* | ||
* @param dependencyKey the id of the dependency node | ||
*/ | ||
void reportResolved(String dependencyKey) { | ||
if (toBeResolved == 0) { | ||
throw new RuntimeException("invalid state - " + this.key() + ": The dependency '" + dependencyKey + "' is already reported or there is no such dependencyKey"); | ||
} | ||
toBeResolved--; | ||
} | ||
} |
156 changes: 156 additions & 0 deletions
156
azure-client-runtime/src/main/java/com/microsoft/azure/DAGraph.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
package com.microsoft.azure; | ||
|
||
import java.util.ArrayDeque; | ||
import java.util.Map; | ||
import java.util.Queue; | ||
|
||
/** | ||
* Type representing a DAG (directed acyclic graph). | ||
* <p> | ||
* each node in a DAG is represented by {@link DAGNode} | ||
* | ||
* @param <T> the type of the data stored in the graph nodes | ||
* @param <U> the type of the nodes in the graph | ||
*/ | ||
public class DAGraph<T, U extends DAGNode<T>> extends Graph<T, U> { | ||
private Queue<String> queue; | ||
private boolean hasParent; | ||
private U rootNode; | ||
|
||
/** | ||
* Creates a new DAG. | ||
* | ||
* @param rootNode the root node of this DAG | ||
*/ | ||
public DAGraph(U rootNode) { | ||
this.rootNode = rootNode; | ||
this.queue = new ArrayDeque<>(); | ||
this.addNode(rootNode); | ||
} | ||
|
||
/** | ||
* @return <tt>true</tt> if this DAG is merged with another DAG and hence has a parent | ||
*/ | ||
public boolean hasParent() { | ||
return hasParent; | ||
} | ||
|
||
/** | ||
* Checks whether the given node is root node of this DAG. | ||
* | ||
* @param node the node {@link DAGNode} to be checked | ||
* @return <tt>true</tt> if the given node is root node | ||
*/ | ||
public boolean isRootNode(U node) { | ||
return this.rootNode == node; | ||
} | ||
|
||
/** | ||
* Merge this DAG with another DAG. | ||
* <p> | ||
* this will mark this DAG as a child DAG, the dependencies of nodes in this DAG will be merged | ||
* with (copied to) the parent DAG | ||
* | ||
* @param parent the parent DAG | ||
*/ | ||
public void merge(DAGraph<T, U> parent) { | ||
this.hasParent = true; | ||
parent.rootNode.addDependency(this.rootNode.key()); | ||
this.rootNode.addDependent(parent.rootNode.key()); | ||
for (Map.Entry<String, U> entry: graph.entrySet()) { | ||
String key = entry.getKey(); | ||
if (!parent.graph.containsKey(key)) { | ||
parent.graph.put(key, entry.getValue()); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Prepares this DAG for traversal using getNext method, each call to getNext returns next node | ||
* in the DAG with no dependencies. | ||
*/ | ||
public void prepare() { | ||
initializeQueue(); | ||
if (queue.isEmpty()) { | ||
throw new RuntimeException("Found circular dependency"); | ||
} | ||
} | ||
|
||
/** | ||
* Gets next node in the DAG which has no dependency or all of it's dependencies are resolved and | ||
* ready to be consumed. | ||
* <p> | ||
* null will be returned when all the nodes are explored | ||
* | ||
* @return next node | ||
*/ | ||
public U getNext() { | ||
return graph.get(queue.poll()); | ||
} | ||
|
||
/** | ||
* Gets the data stored in a graph node with a given key. | ||
* | ||
* @param key the key of the node | ||
* @return the value stored in the node | ||
*/ | ||
public T getNodeData(String key) { | ||
return graph.get(key).data(); | ||
} | ||
|
||
/** | ||
* Reports that a node is resolved hence other nodes depends on it can consume it. | ||
* | ||
* @param completed the node ready to be consumed | ||
*/ | ||
public void reportedCompleted(U completed) { | ||
String dependency = completed.key(); | ||
for (String dependentKey : graph.get(dependency).dependentKeys()) { | ||
DAGNode<T> dependent = graph.get(dependentKey); | ||
dependent.reportResolved(dependency); | ||
if (dependent.hasAllResolved()) { | ||
queue.add(dependent.key()); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* populate dependents of all nodes. | ||
* <p> | ||
* the DAG will be explored in DFS order and all node's dependents will be identified, | ||
* this prepares the DAG for traversal using getNext method, each call to getNext returns next node | ||
* in the DAG with no dependencies. | ||
*/ | ||
public void populateDependentKeys() { | ||
this.queue.clear(); | ||
visit(new Visitor<U>() { | ||
@Override | ||
public void visit(U node) { | ||
if (node.dependencyKeys().isEmpty()) { | ||
queue.add(node.key()); | ||
return; | ||
} | ||
|
||
String dependentKey = node.key(); | ||
for (String dependencyKey : node.dependencyKeys()) { | ||
graph.get(dependencyKey) | ||
.dependentKeys() | ||
.add(dependentKey); | ||
} | ||
} | ||
}); | ||
} | ||
|
||
/** | ||
* Initializes the queue that tracks the next set of nodes with no dependencies or | ||
* whose dependencies are resolved. | ||
*/ | ||
private void initializeQueue() { | ||
this.queue.clear(); | ||
for (Map.Entry<String, U> entry: graph.entrySet()) { | ||
if (!entry.getValue().hasDependencies()) { | ||
this.queue.add(entry.getKey()); | ||
} | ||
} | ||
} | ||
} |
78 changes: 78 additions & 0 deletions
78
azure-client-runtime/src/main/java/com/microsoft/azure/Graph.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package com.microsoft.azure; | ||
|
||
import java.util.HashMap; | ||
import java.util.HashSet; | ||
import java.util.Map; | ||
import java.util.Set; | ||
|
||
/** | ||
* Type representing a directed graph data structure. | ||
* <p> | ||
* each node in a graph is represented by {@link Node} | ||
* | ||
* @param <T> the type of the data stored in the graph's nodes | ||
* @param <U> the type of the nodes in the graph | ||
*/ | ||
public class Graph<T, U extends Node<T>> { | ||
protected Map<String, U> graph; | ||
private Set<String> visited; | ||
|
||
/** | ||
* Creates a directed graph. | ||
*/ | ||
public Graph() { | ||
this.graph = new HashMap<>(); | ||
this.visited = new HashSet<>(); | ||
} | ||
|
||
/** | ||
* Adds a node to this graph. | ||
* | ||
* @param node the node | ||
*/ | ||
public void addNode(U node) { | ||
graph.put(node.key(), node); | ||
} | ||
|
||
/** | ||
* Represents a visitor to be implemented by the consumer who want to visit the | ||
* graph's nodes in DFS order. | ||
* | ||
* @param <U> the type of the node | ||
*/ | ||
interface Visitor<U> { | ||
/** | ||
* visit a node. | ||
* | ||
* @param node the node to visited | ||
*/ | ||
void visit(U node); | ||
} | ||
|
||
/** | ||
* Perform DFS visit in this graph. | ||
* <p> | ||
* The directed graph will be traversed in DFS order and the visitor will be notified as | ||
* search explores each node | ||
* | ||
* @param visitor the graph visitor | ||
*/ | ||
public void visit(Visitor visitor) { | ||
for (Map.Entry<String, ? extends Node<T>> item : graph.entrySet()) { | ||
if (!visited.contains(item.getKey())) { | ||
this.dfs(visitor, item.getValue()); | ||
} | ||
} | ||
visited.clear(); | ||
} | ||
|
||
private void dfs(Visitor visitor, Node<T> node) { | ||
visitor.visit(node); | ||
visited.add(node.key()); | ||
for (String childKey : node.children()) { | ||
if (!visited.contains(childKey)) { | ||
this.dfs(visitor, this.graph.get(childKey)); | ||
} | ||
} | ||
} | ||
} |
62 changes: 62 additions & 0 deletions
62
azure-client-runtime/src/main/java/com/microsoft/azure/Node.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package com.microsoft.azure; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
/** | ||
* Type represents a node in a {@link Graph}. | ||
* | ||
* @param <T> the type of the data stored in the node | ||
*/ | ||
public class Node<T> { | ||
private String key; | ||
private T data; | ||
private List<String> children; | ||
|
||
/** | ||
* Creates a graph node. | ||
* | ||
* @param key unique id of the node | ||
* @param data data to be stored in the node | ||
*/ | ||
public Node(String key, T data) { | ||
this.key = key; | ||
this.data = data; | ||
this.children = new ArrayList<>(); | ||
} | ||
|
||
/** | ||
* @return this node's unique id | ||
*/ | ||
public String key() { | ||
return this.key; | ||
} | ||
|
||
/** | ||
* @return data stored in this node | ||
*/ | ||
public T data() { | ||
return data; | ||
} | ||
|
||
/** | ||
* @return <tt>true</tt> if this node has any children | ||
*/ | ||
public boolean hasChildren() { | ||
return !this.children.isEmpty(); | ||
} | ||
|
||
/** | ||
* @return children (neighbours) of this node | ||
*/ | ||
public List<String> children() { | ||
return this.children; | ||
} | ||
|
||
/** | ||
* @param childKey add a child (neighbour) of this node | ||
*/ | ||
public void addChild(String childKey) { | ||
this.children.add(childKey); | ||
} | ||
} |
Oops, something went wrong.