/*************************GO-LICENSE-START*********************************
* Copyright 2014 ThoughtWorks, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*************************GO-LICENSE-END***********************************/
package com.thoughtworks.go.server.service.dd.reporting;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import com.thoughtworks.go.config.CaseInsensitiveString;
import com.thoughtworks.go.config.materials.dependency.DependencyMaterialConfig;
import com.thoughtworks.go.domain.PipelineTimelineEntry;
import com.thoughtworks.go.domain.StageIdentifier;
import com.thoughtworks.go.domain.materials.MaterialConfig;
import com.thoughtworks.go.domain.materials.dependency.DependencyMaterialRevision;
import com.thoughtworks.go.server.domain.PipelineTimeline;
import com.thoughtworks.go.util.Pair;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
public class ReportingDependencyFanInNode extends ReportingFanInNode {
private static List<Class<? extends MaterialConfig>> DEPENDENCY_NODE_TYPES = new ArrayList<>();
public Set<ReportingFanInNode> children = new HashSet<>();
StageIdentifier currentRevision;
private int totalInstanceCount = Integer.MAX_VALUE;
private int currentCount;
private Map<StageIdentifier, Set<ReportingFaninScmMaterial>> stageIdentifierScmMaterial = new LinkedHashMap<>();
ReportingDependencyFanInNode(MaterialConfig material) {
super(material);
for (Class<? extends MaterialConfig> clazz : DEPENDENCY_NODE_TYPES) {
if (clazz.isAssignableFrom(material.getClass())) {
return;
}
}
throw new RuntimeException("Not a valid root node material type");
}
public void populateRevisions(ReportingFanInGraphContext context) {
initialize(context);
context.out.println("Total Instances: " + totalInstanceCount);
context.out.println();
fillNextRevisions(context);
}
private void setCurrentRevision() {
currentRevision = stageIdentifierScmMaterial.keySet().toArray(new StageIdentifier[0])[0];
}
public void initialize(ReportingFanInGraphContext context) {
totalInstanceCount = context.pipelineTimeline.instanceCount(((DependencyMaterialConfig) materialConfig).getPipelineName());
}
private void fillNextRevisions(ReportingFanInGraphContext context) {
if (!hasMoreInstances()) {
return;
}
final Pair<StageIdentifier, List<ReportingFaninScmMaterial>> sIdScmPair = getRevisionNthFor(1, context);
validateIfRevisionMatchesTheCurrentConfig(context, sIdScmPair);
if (!validateAllScmRevisionsAreSameWithinAFingerprint(sIdScmPair)) {
context.out.println("Latest Revision Is Inconsistent ");
}
context.out.println();
}
private void printCurrentAndOldSCMs(ReportingFanInGraphContext context, Set<MaterialConfig> currentScmMaterials, List<ReportingFaninScmMaterial> scmMaterials,
Pair<StageIdentifier, List<ReportingFaninScmMaterial>> stageIdentifierScmPair) {
context.out.println();
context.out.println("----");
context.out.println("SCM Materials in config:");
context.out.println(currentScmMaterials);
final Set<MaterialConfig> scmMaterialsInRev = new HashSet<>();
for (ReportingFaninScmMaterial scmMaterial : scmMaterials) {
final MaterialConfig scm = context.fingerprintScmMaterialMap.get(scmMaterial.fingerprint);
scmMaterialsInRev.add(scm);
}
context.out.println("----");
context.out.println("SCM Materials in Latest Revision:");
context.out.println(scmMaterialsInRev);
context.out.println("----");
context.out.println("Latest Revision of Material:");
context.out.println(stageIdentifierScmPair.first());
context.out.println("----");
}
private void validateIfRevisionMatchesTheCurrentConfig(ReportingFanInGraphContext context, Pair<StageIdentifier, List<ReportingFaninScmMaterial>> stageIdentifierScmPair) {
if (stageIdentifierScmPair == null) {
return;
}
final Set<MaterialConfig> currentScmMaterials = context.pipelineScmDepMap.get(materialConfig);
final List<ReportingFaninScmMaterial> scmMaterials = stageIdentifierScmPair.last();
printCurrentAndOldSCMs(context, currentScmMaterials, scmMaterials, stageIdentifierScmPair);
final List<ReportingFaninScmMaterial> setOfRevisions = new ArrayList<>();
for (final ReportingFaninScmMaterial scmMaterial : scmMaterials) {
ReportingFaninScmMaterial mat = (ReportingFaninScmMaterial) CollectionUtils.find(setOfRevisions, new Predicate() {
@Override
public boolean evaluate(Object obj) {
if (obj == null) {
return false;
}
ReportingFaninScmMaterial mat = (ReportingFaninScmMaterial) obj;
return scmMaterial.fingerprint.equals(mat.fingerprint)
&& scmMaterial.revision.equals(mat.revision);
}
});
if (mat == null) {
setOfRevisions.add(scmMaterial);
}
}
context.out.println("SCM Revisions of Latest Revision of Material:");
context.out.println(setOfRevisions);
}
private Pair<StageIdentifier, List<ReportingFaninScmMaterial>> getRevisionNthFor(int n, ReportingFanInGraphContext context) {
List<ReportingFaninScmMaterial> scmMaterials = new ArrayList<>();
PipelineTimeline pipelineTimeline = context.pipelineTimeline;
Queue<PipelineTimelineEntry.Revision> revisionQueue = new ConcurrentLinkedQueue<>();
DependencyMaterialConfig dependencyMaterial = (DependencyMaterialConfig) materialConfig;
PipelineTimelineEntry entry = pipelineTimeline.instanceFor(dependencyMaterial.getPipelineName(), totalInstanceCount - n);
StageIdentifier dependentStageIdentifier = dependentStageIdentifier(context, entry, CaseInsensitiveString.str(dependencyMaterial.getStageName()));
if (!StageIdentifier.NULL.equals(dependentStageIdentifier)) {
addToRevisionQueue(entry, revisionQueue, scmMaterials, context);
} else {
return null;
}
while (!revisionQueue.isEmpty()) {
PipelineTimelineEntry.Revision revision = revisionQueue.poll();
DependencyMaterialRevision dmr = DependencyMaterialRevision.create(revision.revision, null);
PipelineTimelineEntry pte = pipelineTimeline.getEntryFor(new CaseInsensitiveString(dmr.getPipelineName()), dmr.getPipelineCounter());
addToRevisionQueue(pte, revisionQueue, scmMaterials, context);
}
return new Pair<>(dependentStageIdentifier, scmMaterials);
}
private boolean validateAllScmRevisionsAreSameWithinAFingerprint(Pair<StageIdentifier, List<ReportingFaninScmMaterial>> pIdScmPair) {
if (pIdScmPair == null) {
return false;
}
List<ReportingFaninScmMaterial> scmMaterialList = pIdScmPair.last();
for (final ReportingFaninScmMaterial scmMaterial : scmMaterialList) {
Collection<ReportingFaninScmMaterial> scmMaterialOfSameFingerprint = CollectionUtils.select(scmMaterialList, new Predicate() {
@Override
public boolean evaluate(Object o) {
return scmMaterial.equals(o);
}
});
for (ReportingFaninScmMaterial faninScmMaterial : scmMaterialOfSameFingerprint) {
if (!faninScmMaterial.revision.equals(scmMaterial.revision)) {
return false;
}
}
}
return true;
}
private StageIdentifier dependentStageIdentifier(ReportingFanInGraphContext context, PipelineTimelineEntry entry, final String stageName) {
return context.pipelineDao.latestPassedStageIdentifier(entry.getId(), stageName);
}
private void addToRevisionQueue(PipelineTimelineEntry entry, Queue<PipelineTimelineEntry.Revision> revisionQueue, List<ReportingFaninScmMaterial> scmMaterials,
ReportingFanInGraphContext context) {
printPipelineTimelineEntry(entry, context);
for (Map.Entry<String, List<PipelineTimelineEntry.Revision>> revisionList : entry.revisions().entrySet()) {
String fingerprint = revisionList.getKey();
PipelineTimelineEntry.Revision revision = revisionList.getValue().get(0);
if (isScmMaterial(fingerprint, context)) {
scmMaterials.add(new ReportingFaninScmMaterial(fingerprint, revision));
continue;
}
if (isDependencyMaterial(fingerprint, context)) {
revisionQueue.add(revision);
}
}
}
private void printPipelineTimelineEntry(PipelineTimelineEntry entry, ReportingFanInGraphContext context) {
context.out.println(
"Pipeline-Timeline-Entry: Id: " + entry.getId() + ", Pipeline-Name: " + entry.getPipelineName() + ", Counter: " + entry.getCounter() + ", Natural-Order: " + entry.naturalOrder());
Iterator it = entry.revisions().entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, List<PipelineTimelineEntry.Revision>> pairs = (Map.Entry) it.next();
context.out.println("Flyweight: " + pairs.getKey() + " - " + pairs.getValue());
}
context.out.println("***");
}
private boolean isDependencyMaterial(String fingerprint, ReportingFanInGraphContext context) {
return context.fingerprintDepMaterialMap.containsKey(fingerprint);
}
private boolean isScmMaterial(String fingerprint, ReportingFanInGraphContext context) {
return context.fingerprintScmMaterialMap.containsKey(fingerprint);
}
private boolean hasMoreInstances() {
return currentCount < totalInstanceCount;
}
enum RevisionAlteration {
NOT_APPLICABLE, SAME_AS_CURRENT_REVISION, ALTERED_TO_CORRECT_REVISION, ALL_OPTIONS_EXHAUSTED,
NEED_MORE_REVISIONS
}
static {
DEPENDENCY_NODE_TYPES.add(DependencyMaterialConfig.class);
}
}