net.aequologica.neo.dagr.DagOnSteroids Maven / Gradle / Ivy
The newest version!
package net.aequologica.neo.dagr;
import static net.aequologica.neo.dagr.DagOnSteroids.NodeCleaner.NodeState.CLEAN;
import static net.aequologica.neo.dagr.DagOnSteroids.NodeCleaner.NodeState.CLEANED;
import static net.aequologica.neo.dagr.DagOnSteroids.NodeCleaner.NodeState.DIRTY;
import static net.aequologica.neo.dagr.bus.BusEvent.Type.CLEAN_ABORTED;
import static net.aequologica.neo.dagr.bus.BusEvent.Type.CLEAN_ERROR;
import static net.aequologica.neo.dagr.bus.BusEvent.Type.CLEAN_OK;
import static net.aequologica.neo.dagr.bus.BusEvent.Type.CLEAN_ORDER_ERROR;
import static net.aequologica.neo.dagr.bus.BusEvent.Type.CLEAN_ORDER_OK;
import static net.aequologica.neo.dagr.bus.BusEvent.Type.CLEAN_STARTED;
import static net.aequologica.neo.dagr.bus.BusEvent.Type.INITIALIZE;
import static net.aequologica.neo.dagr.bus.BusEvent.Type.MAGIC_CLEAN;
import static net.aequologica.neo.dagr.bus.BusEvent.Type.SMUDGE;
import static net.aequologica.neo.dagr.bus.BusEvent.Type.TERMINATE;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.reactivestreams.Subscription;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import net.aequologica.neo.dagr.DagOnSteroids.DagCleaner.Journal;
import net.aequologica.neo.dagr.DagOnSteroids.NodeCleaner.NodeCleanerException;
import net.aequologica.neo.dagr.DagOnSteroids.NodeCleaner.NodeCleaningResult;
import net.aequologica.neo.dagr.DagOnSteroids.NodeCleaner.NodeState;
import net.aequologica.neo.dagr.bus.Bus;
import net.aequologica.neo.dagr.model.Dag;
import net.aequologica.neo.dagr.model.Dag.Bump;
import net.aequologica.neo.dagr.model.Dag.Bumper;
import net.aequologica.neo.dagr.model.Dag.Gucrid;
import net.aequologica.neo.dagr.model.Dag.Node;
import de.skuzzle.semantic.Version;
import com.pivovarit.function.ThrowingBiFunction;
public class DagOnSteroids {
private static final Logger LOG = LoggerFactory.getLogger(DagOnSteroids.class);
private static final SimpleDateFormat FMT = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
private static final String SIMPLENAME = DagOnSteroids.class.getSimpleName();
ThrowingBiFunction, Node, NodeCleaner.NodeCleaningResult, NodeCleaner.NodeCleanerException> cleaningFunction = (b, n) -> NodeCleaner.NodeCleaningResult.undefined(b.getKey());
final private Dag dag;
final private Map properties;
final private EnumMap nameTemplates;
final private EnumMap dagCleaners;
final public Bumper bumper;
private Optional subDagId;
public DagOnSteroids(final Dag dag, Map nodeAliases, Map properties) {
this.dag = dag;
this.dagCleaners = new EnumMap(Scope.class);
this.nameTemplates = new EnumMap(Scope.class);
this.properties = properties != null ? properties : Collections.emptyMap();
for (Scope scope : Scope.values()) {
this.dagCleaners.put(scope, new DagCleaner(new BusImpl(this, scope)));
this.nameTemplates.put(scope, "{{node.name}}-"+scope.toString());
}
Map nameTemplatesFromProperties = parseMap(this.properties.get("nameTemplates"));
for (Map.Entry e : nameTemplatesFromProperties.entrySet()) {
this.nameTemplates.put(Scope.valueOf(e.getKey()), e.getValue());
}
if (nodeAliases != null) {
for (Node node : dag.getNodes()) {
node.setAlias(nodeAliases.get(node.getName()));
}
}
this.bumper = new Bumper(this.properties.get("bumps"));
this.subDagId = Optional.ofNullable(null);
}
public Dag getDag() {
return this.dag;
}
public Map getProperties() {
return this.properties;
}
public EnumMap getNameTemplates() {
return nameTemplates;
}
public String getSubDagId() {
return subDagId.orElse(null);
}
public void setSubDagId(String subDagId) {
if (subDagId != null && dag.getSubDag(subDagId) != null) {
this.subDagId = Optional.ofNullable(subDagId);
} else {
this.subDagId = Optional.ofNullable(null);
}
}
public List>> getBusEntries() {
return this.dagCleaners.entrySet().stream().map(e -> new AbstractMap.SimpleImmutableEntry<>(e.getKey(), e.getValue().getBus())).collect(Collectors.toList());
}
public EnumMap getDagCleaners() {
return this.dagCleaners;
}
public DagCleaner getDagCleaner(Scope scope) {
return this.dagCleaners.get(scope);
}
public ThrowingBiFunction, Node, NodeCleaner.NodeCleaningResult, NodeCleaner.NodeCleanerException> getCleaningFunction() {
return this.cleaningFunction;
}
public void setCleaningFunction(ThrowingBiFunction, Node, NodeCleaner.NodeCleaningResult, NodeCleaner.NodeCleanerException> cleaningFunction) {
this.cleaningFunction = cleaningFunction;
}
public NodeCleaningResult clean(Scope scope, Node node, Boolean skipRelease) throws NodeCleanerException {
if (cleaningFunction == null) {
throw new NodeCleanerException("no cleaningFunction defined in enclosing dag");
}
return cleaningFunction.apply(new AbstractMap.SimpleImmutableEntry<>(scope, skipRelease), node);
}
/**
* actually, this enum should be embedded in DagCleaner class,
* as it is meaningful only within DagCleaner context,
* but java des not allow enum in non-static classes
*/
public enum State {
RUNNING, DONE, CANCELLED;
}
final public class DagCleaner {
final private Bus bus;
final private Map nodeCleanerMap;
private Journal journal;
private Subscription subscription;
private String sub;
final private Set nodesToBeSmudgedSkipRelease;
public DagCleaner(Bus bus) {
this.bus = bus;
if (bus.getScope().equals(Scope.RELEASE)) {
nodesToBeSmudgedSkipRelease = new HashSet<>();
} else {
nodesToBeSmudgedSkipRelease = Collections.emptySet();
}
this.nodeCleanerMap = new HashMap<>();
for (Node node : dag.getNodes()) {
nodeCleanerMap.put(node, new NodeCleaner(this.bus.getScope(), node, NodeState.UNKNOWN));
}
}
public Bus getBus() {
return this.bus;
}
public String getSub() {
return sub;
}
public NodeCleaner getNodeCleaner(Node node) {
return nodeCleanerMap.get(node);
}
public void cleanAll() {
cleanSubDag(null);
}
public void cleanSubDag(String subDagKey) {
if (getCleaningFunction() == null) {
throw new RuntimeException("No cleaner defined. Check configuration.");
}
if (this.journal == null) {
this.journal = new Journal();
} else {
if (this.journal.state.equals(State.RUNNING)) {
throw new RuntimeException("Cleaning already running: Wait for completion, or cancel it and retry.");
}
}
this.sub = subDagKey;
final Set nodesToInclude = dag.getNodes(subDagKey) // return all nodes if subDagKey is null
.stream()
.filter(n -> !n.getClazz().equals("cluster"))
.map(n -> n.getName())
.collect(Collectors.toSet());
synchronized (journal) {
this.journal.raz();
safeWrite(this.journal, "start first round");
final Set nodesToBeSmudged = new HashSet<>();
final Set nodesToBeMagicCleaned = new HashSet<>();
{
if (bus.getScope().equals(Scope.RELEASE)) {
nodesToBeSmudgedSkipRelease.clear();
}
for (Node node : dag.getTopologicalNodes()) {
if ( node.getValue() != null &&
node.getValue().getGucrid() != null &&
!node.getValue().getGucrid().contains("SNAPSHOT")) {
// force CLEAN state if not a snapshot
nodesToBeMagicCleaned.add(node);
explainMagicClean(node, "not a snapshot", node.getValue().getGucrid().toString());
} else if (!nodesToInclude.contains(node.getName())){
// force CLEAN state if node is not included (e.g. sub dags)
nodesToBeMagicCleaned.add(node);
explainMagicClean(node, "not in sub dag", nodesToInclude.toString());
} else {
// SMUDGE everything else
nodesToBeSmudged.add(node);
}
}
{
// in RELEASE scope,
// every node
// 1. that is *not* in the sub graph (i.e. magic-cleaned)
// 2. which has at least one smudged node as predecessor
// must be smudged and the clean operation flagged with "skipRelease"
if (bus.getScope().equals(Scope.RELEASE)) {
for (Node smudged : nodesToBeSmudged) {
Iterator bfi = dag.getBreadthFirstIterator(smudged);
while (bfi.hasNext()){
Node succ = bfi.next();
if (nodesToBeMagicCleaned.contains(succ)) {
nodesToBeMagicCleaned.remove(succ);
nodesToBeSmudgedSkipRelease.add(succ);
}
}
}
}
}
}
bus.send(MAGIC_CLEAN, nodesToBeMagicCleaned, SIMPLENAME);
bus.send(SMUDGE, nodesToBeSmudged, SIMPLENAME);
bus.send(SMUDGE, nodesToBeSmudgedSkipRelease, SIMPLENAME);
for (Node node : dag.getTopologicalNodes()) {
safeWrite(this.journal, String.format("set node /%s/ state to %s",
node.getName(),
getNodeCleaner(node).getState()));
}
bus.toObservable().filter( event -> event.getType().equals(CLEAN_STARTED) ||
event.getType().equals(CLEAN_OK) ||
event.getType().equals(CLEAN_ERROR) ||
event.getType().equals(CLEAN_ABORTED)).subscribe(event -> {
Node node = event.get();
safeWrite(this.journal, String.format("received event %s on node /%s/ from [ %s ]",
event.getType(),
node.getName(),
event.getSource()
));
if (journal != null && !journal.state.equals(State.RUNNING)) {
safeWrite(this.journal, String.format("Cleaning is not running (state=%s), doing nothing.", journal.state.toString()));
return;
}
if (event.getType().equals(CLEAN_OK)) {
queue();
} else if (event.getType().equals(CLEAN_ERROR) ||
event.getType().equals(CLEAN_ABORTED)) {
error(event.get());
}
ifAllNodesAreCleanThenDone();
});
queue();
}
}
private void explainMagicClean(Node node, String reason, String optional) {
safeWrite(this.journal, String.format("set node /%s/ state to %s: %s%s",
node.getName(),
CLEAN,
reason,
optional != null ? (" - " + optional) : ""
));
}
public String getJournalAsString() {
if (journal != null) {
return journal.toString();
} else {
return "nothing to read";
}
}
public void done() {
try {
clean(this.bus.getScope(), null, null);
safeWrite(this.journal, "ordered closing of cleaning process");
} catch (NodeCleanerException e) {
safeWrite(this.journal, String.format("exception while ordering closing of cleaning process in scope &s : %s", this.bus.getScope(), e.getMessage()));
throw new RuntimeException(e);
}
if (subscription != null) {
subscription.cancel();
subscription = null;
}
if (journal != null) {
journal.done();
}
}
public void error(Node error) {
if (subscription != null) {
subscription.cancel();
subscription = null;
}
if (journal != null) {
journal.error(error);
journal.close();
}
}
public void cancel() {
if (subscription != null) {
subscription.cancel();
subscription = null;
}
if (journal != null) {
journal.cancel();
}
}
private void ifAllNodesAreCleanThenDone() {
if (journal == null) {
return;
}
if (!journal.state.equals(State.RUNNING)) {
return;
}
int doneCount = 0;
final String subDag = bus.getScope().equals(Scope.RELEASE) ? null : getSubDagId(); // get all nodes for RELEASE scope
final Collection nodesSubSet = dag.getNodes(subDag);
for (Node node : nodesSubSet) {
NodeState state = getNodeCleaner(node).getState();
if (state != null) {
if (state.oneOf(CLEAN, CLEANED)) {
doneCount++;
}
}
}
if (doneCount == nodesSubSet.size()) {
done();
}
}
private void orderCleaningOfNode(Node node, Boolean skipRelease) {
NodeCleaner.NodeCleaningResult result;
try {
result = clean(bus.getScope(), node, skipRelease);
} catch (Exception e) {
URI source = null;
if (e instanceof NodeCleanerException) {
source = ((NodeCleanerException)e).getSource();
}
LOG.error("ordering cleaning of node /{}/ failed with exception \"{}\". (caught exception is not rethrown but cleanNode function will return not zero + exception message). source={}", node.getName(), e.getMessage(), source);
result = NodeCleaner.NodeCleaningResult.fromException(bus.getScope(), e, source);
}
if (result != null && result.asBoolean()) {
bus.send(CLEAN_ORDER_OK, Collections.singletonList(node), SIMPLENAME);
safeWrite(this.journal, String.format("ordered cleaning of node /%s/ (%s)",
node.getName(),
result.getURI()));
} else {
String error = (result == null ? "null result" : result.getStatus() + " " + result.getReason());
bus.send(CLEAN_ORDER_ERROR, Collections.singletonList(node), error);
safeWrite(this.journal, String.format("cannot order cleaning of node /%s/: %s [%s]",
node.getName(),
error,
result.getURI()
));
}
}
private void queue() {
safeWrite(this.journal, "start inpecting queue");
for (Node node : dag.getTopologicalNodes()) {
final NodeState thisState = getNodeCleaner(node).getState();
// check only dirty nodes
if (!thisState.oneOf(DIRTY)) {
continue;
}
// inspect predecessors
final Collection predecessors = dag.predecessorsOf(node);
int doneCount = 0;
int errorCount = 0;
for (final Node pred : predecessors) {
final NodeState predState = getNodeCleaner(pred).getState();
if (predState != null) {
if (predState.oneOf(CLEAN, CLEANED)) {
doneCount++;
}
}
}
// all predecessors are clean
if (doneCount == predecessors.size()) {
if (predecessors.size() == 0) {
safeWrite(this.journal, String.format("node /%s/ is %s and has no predecessor : time to clean it, isn't it?",
node.getName(),
thisState
));
} else {
safeWrite(this.journal, String.format("node /%s/ is %s and all its predecessors (%d) are DONE (with %d error(s)) : time to clean it, isn't it?",
node.getName(),
thisState,
doneCount,
errorCount
));
}
orderCleaningOfNode(node, nodesToBeSmudgedSkipRelease.contains(node));
}
}
safeWrite(this.journal, "done inpecting queue");
}
class Journal implements Closeable {
private State state;
private ByteArrayOutputStream baos;
private BufferedWriter writer;
Journal() {
raz();
}
void raz() {
bus.send(INITIALIZE, Collections.emptyList(), SIMPLENAME);
this.state = State.RUNNING;
this.baos = new ByteArrayOutputStream();
this.writer = new BufferedWriter(new OutputStreamWriter(baos, StandardCharsets.UTF_8));
}
void write(String entry) {
try {
writer.newLine();
writer.write(FMT.format(new Date()));
writer.write(" [");
writer.write(Thread.currentThread().getName());
writer.write("] ");
writer.write(entry);
writer.flush();
} catch (IOException e) {
LOG.warn("ignored exception {}", e.getMessage());
}
}
void done() {
write("done!");
this.state = State.DONE;
close();
}
void cancel() {
write("cancelled by external action");
this.state = State.CANCELLED;
close();
}
void error(Node node) {
write("node [ " + node.getName() + " ]");
}
@Override
public void close() {
for (Node node : dag.getTopologicalNodes()) {
write("node /" + node.getName() + "/ state is " + getNodeCleaner(node).getState().toString());
}
bus.send(TERMINATE, Collections.emptyList(), SIMPLENAME);
}
@Override
public String toString() {
return this.state.toString() + "\n"+ new String(baos.toByteArray(), StandardCharsets.UTF_8);
}
}
public State getState() {
if (this.journal == null) {
return null;
}
return this.journal.state;
}
}
static void safeWrite(Journal journal, String entry) {
LOG.debug(entry);
if (journal == null) {
return;
}
journal.write(entry);
}
static public List getNodesFromNameAndBranchContains(final Dag dag, final String nodeName, final String branchSubstring) {
final List ret = new LinkedList<>();
for (Node node : dag.getNodesFromName(nodeName)) {
if (!containsBranchSubstring(node, branchSubstring)) {
continue;
}
ret.add(node);
}
LOG.debug("found {} nodes with nodeName='{}' and branch='{}' in dag '{}'", ret.size(), nodeName, branchSubstring, dag.getName());
return ret;
}
static private boolean containsBranchSubstring(final Node node, final String branchSubstring) {
final String nodeBranch;
if (node != null &&
node.getValue() != null &&
node.getValue().getBranch() != null) {
nodeBranch = node.getValue().getBranch();
} else {
nodeBranch = null;
}
if (nodeBranch == null && branchSubstring == null) {
return true;
}
if (nodeBranch == null && branchSubstring == null) {
return false;
}
return nodeBranch.contains(branchSubstring);
}
static final private Pattern DAGNAMECONTAINSSUB = Pattern.compile("(.*)%2Fsubs%2(.*)");
static public String[] parseName(String dagName) {
// e.g. development.neo.ondemand.com%2Fsubs%2F124966568
// to test regexps : http://www.regexplanet.com/advanced/java/index.html
Matcher dagNameContainsSub = DAGNAMECONTAINSSUB.matcher(dagName);
if (dagNameContainsSub.matches()) {
return new String[] {dagNameContainsSub.group(1), dagNameContainsSub.group(2)};
} else {
return new String[] {dagName, null};
}
}
@JsonIgnoreProperties(ignoreUnknown=true)
public static class NodeCleaner {
@JsonProperty
private UUID id;
@JsonProperty
final private Node node;
@JsonProperty
final private Scope scope;
@JsonProperty
private NodeState state;
@JsonIgnore
private Long start;
@JsonIgnore
private Long stop;
@JsonCreator
public NodeCleaner(
@JsonProperty(value="scope") final Scope scope,
@JsonProperty(value="node") final Node node,
@JsonProperty(value="state") final NodeState state) {
this.node = node;
this.scope = scope;
this.state = state;
}
public String getId() {
return this.id == null ? null : this.id.toString();
}
public void setId(String id) {
if (id == null) {
this.id = null;
if (this.start != null) {
this.stop = new Date().getTime();
}
} else {
this.start = new Date().getTime();
this.stop = null;
if (id.matches("[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}")) {
this.id = UUID.fromString(id);
}
}
}
@JsonIgnore
public Node getNode() {
return node;
}
public NodeState getState() {
return state;
}
public void setState(NodeState state) {
this.state = state;
}
public Long getDuration() {
if (start == null) {
return null;
} else if (stop == null) {
return new Date().getTime() - start;
}
return stop - start;
}
public enum NodeState {
// at start
@JsonProperty("UNKNOWN") UNKNOWN,
@JsonProperty("CLEAN") CLEAN,
@JsonProperty("DIRTY") DIRTY,
// doing something
@JsonProperty("CLEANING_ORDERED") CLEANING_ORDERED,
@JsonProperty("BEING_CLEANED") BEING_CLEANED,
// at end
@JsonProperty("UNCLEANABLE") UNCLEANABLE,
@JsonProperty("CLEANED") CLEANED,
@JsonProperty("FAIL") FAIL,
@JsonProperty("ABORTED") ABORTED;
public boolean oneOf(NodeState ... others) {
for (NodeState other : others) {
if (this.equals(other)) {
return true;
}
}
return false;
}
}
public static class NodeCleanerException extends Exception {
private static final long serialVersionUID = -3468108554682441131L;
final public URI source;
public NodeCleanerException() {
this((URI)null);
}
public NodeCleanerException(String arg0, Throwable arg1, boolean arg2, boolean arg3) {
this((URI)null, arg0, arg1, arg2, arg3);
}
public NodeCleanerException(String arg0, Throwable arg1) {
this((URI)null, arg0, arg1);
}
public NodeCleanerException(String arg0) {
this((URI)null, arg0);
}
public NodeCleanerException(Throwable arg0) {
this((URI)null, arg0);
}
public NodeCleanerException(URI source, String arg0, Throwable arg1, boolean arg2, boolean arg3) {
super(addSourceToMessage(arg0, source), arg1, arg2, arg3);
this.source = source;
}
public NodeCleanerException(URI source, String arg0, Throwable arg1) {
super(addSourceToMessage(arg0, source), arg1);
this.source = source;
}
public NodeCleanerException(URI source, String arg0) {
super(addSourceToMessage(arg0, source));
this.source = source;
}
public NodeCleanerException(URI source, Throwable arg0) {
super(sourceAsMessage(source), arg0);
this.source = source;
}
public NodeCleanerException(URI source) {
super(sourceAsMessage(source));
this.source = source;
}
public URI getSource() {
return source;
}
static private final String addSourceToMessage(String message, URI source) {
return message + (source!=null ? " - source: " + source.toString() : "");
}
static private final String sourceAsMessage(URI source) {
return (source!=null ? "source: " + source.toString() : "");
}
}
@JsonIgnoreProperties(ignoreUnknown=true)
public static class NodeCleaningResult {
private final Scope scope;
private final int status;
private final String reason;
private final URI uri;
@JsonCreator
public NodeCleaningResult(
@JsonProperty(value="bus") Scope scope,
@JsonProperty(value="status") int status,
@JsonProperty(value="reason") String reason,
@JsonProperty(value="uri") URI uri) {
this.scope = scope;
this.status = status;
this.reason = reason;
this.uri = uri;
}
public Scope getScope() {
return scope;
}
public int getStatus() {
return status;
}
public String getReason() {
return reason;
}
public URI getURI() {
return uri;
}
@JsonIgnore
public boolean asBoolean() {
return (this.status == 0 || this.status/100 == 2);
}
static public NodeCleaningResult undefined(Scope c) {
return new NodeCleaningResult(c, -1, "undefined", null);
}
public static NodeCleaningResult fromException(Scope c, Exception e, URI uri) {
String msg = e.getClass().getSimpleName() + "| " + e.getMessage();
if (e.getCause() != null) {
msg = msg + "\n(cause:" +e.getCause().getClass().getSimpleName()+"| "+e.getCause().getMessage() + ")";
}
return new NodeCleaningResult(c, -1, msg, uri);
}
public static NodeCleaningResult ok(Scope c, URI uri) {
return new NodeCleaningResult(c, 0, "", uri);
}
public static NodeCleaningResult from(Scope c, int status, String reason, URI uri) {
return new NodeCleaningResult(c, status, reason, uri);
}
}
}
@SuppressWarnings("unused")
private static String formatMap(Map map) {
if (map == null || map.size() == 0) {
return "";
}
return Joiner.on(",").withKeyValueSeparator("=").join(map);
}
private static Map parseMap(String formattedMap) {
if (formattedMap == null || formattedMap.length() == 0) {
return Collections.emptyMap();
}
return Splitter.on(",").withKeyValueSeparator("=").split(formattedMap);
}
@JsonIgnoreProperties
static public class NodeNameVersion {
@JsonProperty
final public String name;
@JsonProperty
final public String version;
static public NodeNameVersion create(final String name, final String version) {
return new NodeNameVersion(name, version);
}
public NodeNameVersion(final String name, final String version) {
this.name = name;
this.version = version;
}
}
public NodeNameVersion getCurrent(Node node, boolean range) {
if (node != null &&
node.getName() != null &&
!node.getName().trim().isEmpty() &&
node.getValue() != null ) {
String gucridAsString = node.getValue().getGucrid();
if (gucridAsString == null || gucridAsString.trim().isEmpty()) {
return null;
}
final Gucrid gucrid = Gucrid.create(gucridAsString);
if (gucrid == null || gucrid.getVersion() == null|| !gucrid.getVersion().isPresent()) {
return null;
}
String v = gucrid.getVersion().get().toString();
if (range) {
v = "[,"+v+"]";
}
return NodeNameVersion.create(node.getName(), v);
}
return null;
}
public Collection getCurrents(boolean range) {
List currentNodeNameVersions = new ArrayList<>();
for (Node node : getDag().getNodes()) {
NodeNameVersion nodeNameVersion = getCurrent(node, range);
if (nodeNameVersion != null) {
currentNodeNameVersions.add(nodeNameVersion);
}
}
return currentNodeNameVersions;
}
public NodeNameVersion getNext(Node node, boolean range, boolean allowSnapshots, boolean forbidSnapshotFromRelease) {
if (node != null &&
node.getName() != null &&
!node.getName().trim().isEmpty() &&
node.getValue() != null ) {
String gucridAsString = node.getValue().getGucrid();
if (gucridAsString == null || gucridAsString.trim().isEmpty()) {
return null;
}
final Gucrid gucrid = Gucrid.create(gucridAsString);
if (gucrid == null || gucrid.getSemanticVersion() == null|| !gucrid.getSemanticVersion().isPresent()) {
return null;
}
Bump bump = bumper.fromNodeName(node.getName());
if (bump == null) {
return null;
}
Version releaseVersion = gucrid.getReleaseVersion();
if (releaseVersion == null) {
return null;
}
Version nextDevelopmentVersion = gucrid.getNextDevelopmentVersion(bump);
if (nextDevelopmentVersion == null) {
return null;
}
String v;
if (!allowSnapshots || (forbidSnapshotFromRelease && !gucridAsString.contains("-SNAPSHOT")) ) {
v = releaseVersion.toString();
} else {
v = nextDevelopmentVersion.toString();
}
if (range) {
v = "[,"+v+"]";
}
return NodeNameVersion.create(node.getName(), v);
}
return null;
}
public Collection getNexts(boolean range, boolean allowSnapshots) {
List nextNodeNameVersions = new ArrayList<>();
for (Node node : getDag().getNodes()) {
final NodeNameVersion nodeNameVersion = getNext(node, range, allowSnapshots, true);
if (nodeNameVersion != null) {
nextNodeNameVersions.add(nodeNameVersion);
}
}
return nextNodeNameVersions;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy