/* * Copyright 2017 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. */ package com.thoughtworks.go.config; import com.thoughtworks.go.config.materials.MaterialConfigs; import com.thoughtworks.go.config.remote.ConfigReposConfig; import com.thoughtworks.go.domain.materials.MaterialConfig; import com.thoughtworks.go.domain.packagerepository.PackageRepository; import com.thoughtworks.go.domain.scm.SCM; import java.util.HashMap; /** * @understands providing right state required to validate a given config element */ public class ConfigSaveValidationContext implements ValidationContext{ private final Validatable immediateParent; private final ConfigSaveValidationContext parentContext; private HashMap<Class, Object> objectOfType; private HashMap<String, MaterialConfigs> fingerprintToMaterials = null; public ConfigSaveValidationContext(Validatable immediateParent) { this(immediateParent, null); } public ConfigSaveValidationContext(Validatable immediateParent, ConfigSaveValidationContext parentContext) { this.immediateParent = immediateParent; this.parentContext = parentContext; objectOfType = new HashMap<>(); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof ConfigSaveValidationContext)) { return false; } ConfigSaveValidationContext that = (ConfigSaveValidationContext) o; if (immediateParent != null ? !immediateParent.equals(that.immediateParent) : that.immediateParent != null) { return false; } if (parentContext != null ? !parentContext.equals(that.parentContext) : that.parentContext != null) { return false; } return true; } @Override public int hashCode() { int result = immediateParent != null ? immediateParent.hashCode() : 0; result = 31 * result + (parentContext != null ? parentContext.hashCode() : 0); return result; } @Override public ConfigSaveValidationContext withParent(Validatable current) { return new ConfigSaveValidationContext(current, this); } private CruiseConfig getCruiseConfig() { return loadFirstOfType(CruiseConfig.class); } @Override public ConfigReposConfig getConfigRepos() { return getCruiseConfig().getConfigRepos(); } public JobConfig getJob() { return loadFirstOfType(JobConfig.class); } private <T> T loadFirstOfType(Class<T> klass) { T obj = getFirstOfType(klass); if (obj == null) { throw new IllegalArgumentException(String.format("Could not find an object of type '%s'.", klass.getCanonicalName())); } return obj; } private <T> T getFirstOfType(Class<T> klass) { Object o = objectOfType.get(klass); if (o == null) { o = _getFirstOfType(klass); objectOfType.put(klass, o); } return (T) o; } private <T> T _getFirstOfType(Class<T> klass) { if (parentContext != null) { T obj = parentContext.getFirstOfType(klass); if (obj != null) { return obj; } } if (immediateParent == null) { return null; } else if (immediateParent.getClass().equals(klass)) { return (T) immediateParent; } else { // added because of higher hierarchy of configuration types. // now there are interfaces with more than one implementation // so when asking for CruiseConfig there are 2 matching classes - BasicCruiseConfig and MergeCruiseConfig Class<?>[] interfacesOfCandidate = immediateParent.getClass().getInterfaces(); for(Class<?> inter : interfacesOfCandidate) { if(inter.equals(klass)) { // candidate implements interface whose instances we are looking for return (T) immediateParent; } } } return null; } public StageConfig getStage() { return loadFirstOfType(StageConfig.class); } public PipelineConfig getPipeline() { return loadFirstOfType(PipelineConfig.class); } public PipelineTemplateConfig getTemplate() { return loadFirstOfType(PipelineTemplateConfig.class); } @Override public PipelineConfig getPipelineConfigByName(CaseInsensitiveString pipelineName) { return getCruiseConfig().getPipelineConfigByName(pipelineName); } @Override public boolean shouldCheckConfigRepo() { return true; } @Override public SecurityConfig getServerSecurityConfig() { return getCruiseConfig().server().security(); } @Override public boolean doesTemplateExist(CaseInsensitiveString template) { return getCruiseConfig().getTemplates().hasTemplateNamed(template); } @Override public SCM findScmById(String scmID) { return getCruiseConfig().getSCMs().find(scmID); } @Override public PackageRepository findPackageById(String packageId) { return getCruiseConfig().getPackageRepositories().findPackageRepositoryHaving(packageId); } public String getParentDisplayName() { return getParentType().getAnnotation(ConfigTag.class).value(); } private Class<? extends Validatable> getParentType() { return getParent().getClass(); } public Validatable getParent() { return immediateParent; } public boolean isWithinTemplates() { return hasParentOfType(TemplatesConfig.class); } public boolean isWithinPipelines() { return hasParentOfType(PipelineConfigs.class); } private <T> boolean hasParentOfType(Class<T> validatable) { return getFirstOfType(validatable) != null; } public PipelineConfigs getPipelineGroup() { return loadFirstOfType(PipelineConfigs.class); } @Override public String toString() { return "ValidationContext{" + "immediateParent=" + immediateParent + ", parentContext=" + parentContext + '}'; } @Override public boolean isValidProfileId(String profileId) { return this.getCruiseConfig().server().getElasticConfig().getProfiles().find(profileId) != null; } @Override public boolean shouldNotCheckRole() { return isWithinTemplates(); } public static ConfigSaveValidationContext forChain(Validatable... validatables) { ConfigSaveValidationContext tail = new ConfigSaveValidationContext(null); for (Validatable validatable : validatables) { tail = tail.withParent(validatable); } return tail; } public MaterialConfigs getAllMaterialsByFingerPrint(String fingerprint) { if (fingerprintToMaterials == null || fingerprintToMaterials.isEmpty()) { primeForMaterialValidations(); } MaterialConfigs matchingMaterials = fingerprintToMaterials.get(fingerprint); return matchingMaterials == null ? new MaterialConfigs() : matchingMaterials; } private void primeForMaterialValidations() { CruiseConfig cruiseConfig = getCruiseConfig(); fingerprintToMaterials = new HashMap<>(); for (PipelineConfig pipelineConfig : cruiseConfig.getAllPipelineConfigs()) { for (MaterialConfig material : pipelineConfig.materialConfigs()) { String fingerprint = material.getFingerprint(); if (!fingerprintToMaterials.containsKey(fingerprint)) { fingerprintToMaterials.put(fingerprint, new MaterialConfigs()); } MaterialConfigs materialsForFingerprint = fingerprintToMaterials.get(fingerprint); materialsForFingerprint.add(material); } } } }