com.yahoo.config.model.producer.AnyConfigProducer Maven / Gradle / Ivy
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.config.model.producer;
import com.yahoo.config.ConfigInstance;
import com.yahoo.config.model.ApplicationConfigProducerRoot;
import com.yahoo.config.model.deploy.DeployState;
import com.yahoo.config.subscription.ConfigInstanceUtil;
import com.yahoo.vespa.config.ConfigDefinitionKey;
import com.yahoo.vespa.config.ConfigPayload;
import com.yahoo.vespa.config.ConfigPayloadBuilder;
import com.yahoo.vespa.config.ConfigTransformer;
import com.yahoo.vespa.config.GenericConfig;
import com.yahoo.vespa.model.ConfigProducer;
import com.yahoo.vespa.model.HostSystem;
import com.yahoo.vespa.model.Service;
import com.yahoo.vespa.model.admin.Admin;
import com.yahoo.vespa.model.admin.monitoring.Monitoring;
import java.io.Serializable;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Superclass for all config producers.
* Config producers constructs and returns config instances on request.
*
* @author gjoranv
* @author arnej
*/
public abstract class AnyConfigProducer
implements ConfigProducer, ConfigInstance.Producer, Serializable {
public static final Logger log = Logger.getLogger(AnyConfigProducer.class.getPackage().toString());
private final String subId;
private String configId = null;
private TreeConfigProducer parent = null;
private UserConfigRepo userConfigs = new UserConfigRepo();
protected static boolean stateIsHosted(DeployState deployState) {
return (deployState != null) && deployState.isHosted();
}
/**
* Creates a new AnyConfigProducer with the given parent and subId.
* This constructor will add the resulting producer to the children of parent.
*
* @param parent the parent of this ConfigProducer
* @param subId the fragment of the config id for the producer
*/
public AnyConfigProducer(TreeConfigProducer parent, String subId) {
this(subId);
if (parent != null) {
parent.addChild(this);
}
}
/** Removes this from the config model */
protected void remove() {
if (parent != null)
parent.removeChild(this);
}
protected final void setParent(TreeConfigProducer parent) {
this.parent = parent;
computeConfigId();
}
public final String getSubId() { return subId; }
/**
* Create an config producer with a configId only. Used e.g. to create root nodes, and producers
* that are given parents after construction using {@link TreeConfigProducer#addChild(AnyConfigProducer)}.
*
* @param subId The sub configId. Note that this can be prefixed when calling addChild with this producer as arg.
*/
public AnyConfigProducer(String subId) {
if (subId.indexOf('/') != -1) {
throw new IllegalArgumentException("A subId might not contain '/' : '" + subId + "'");
}
this.subId = subId;
}
/**
* Sets the user configs for this producer.
*
* @param repo User configs repo.
*/
public void setUserConfigs(UserConfigRepo repo) { this.userConfigs = repo; }
/** Returns the user configs of this */
@Override
public UserConfigRepo getUserConfigs() { return userConfigs; }
/**
* ConfigProducers that must have a special config id should use
* setConfigId() instead of overloading this method. This is
* because config IDs must be registered through setConfigId().
*/
public final String getConfigId() {
if (configId == null) throw new IllegalStateException("The system topology must be frozen first.");
return configId;
}
protected final String currentConfigId() {
return configId;
}
/**
* Sets the config id for this producer. Will also add this
* service to the root node, so the new config id will be picked
* up. Note that this producer will be known with both the old
* and the new config id in the root node after using this method.
*/
protected void addConfigId(String id) {
if (id == null) throw new NullPointerException("Config ID cannot be null.");
getRoot().addDescendant(id, this);
if (!isVespa() && (getVespa() != null))
getVespa().addDescendant(this);
}
@Override
public final boolean cascadeConfig(ConfigInstance.Builder builder) {
boolean found = false;
if (parent != null)
found = parent.cascadeConfig(builder);
boolean foundHere = builder.dispatchGetConfig(this);
log.log(Level.FINE, () -> "cascadeconfig in " + this + ", getting config " +
builder.getClass().getDeclaringClass().getName() + " for config id '" + configId +
"' found here=" + foundHere);
found = found || foundHere;
return found;
}
@Override
public final boolean addUserConfig(ConfigInstance.Builder builder) {
boolean didApply = false;
if (parent != null) {
didApply = parent.addUserConfig(builder);
}
log.log(Level.FINEST, () -> "User configs is: " + userConfigs.toString());
// TODO: What do we do with md5. Currently ignored for user configs?
ConfigDefinitionKey key = new ConfigDefinitionKey(builder.getDefName(), builder.getDefNamespace());
if (userConfigs.get(key) != null) {
log.log(Level.FINEST, () -> "Apply in " + configId);
applyUserConfig(builder, userConfigs.get(key));
didApply = true;
}
return didApply;
}
private void applyUserConfig(ConfigInstance.Builder builder, ConfigPayloadBuilder payloadBuilder) {
ConfigInstance.Builder override;
if (builder instanceof GenericConfig.GenericConfigBuilder) {
// Means that the builder is unknown and that we should try to apply the payload without
// the real builder
override = getGenericConfigBuilderOverride((GenericConfig.GenericConfigBuilder) builder, payloadBuilder);
} else {
override = getConfigInstanceBuilderOverride(builder, ConfigPayload.fromBuilder(payloadBuilder));
}
ConfigInstanceUtil.setValues(builder, override);
}
private ConfigInstance.Builder getGenericConfigBuilderOverride(GenericConfig.GenericConfigBuilder builder, ConfigPayloadBuilder payloadBuilder) {
ConfigDefinitionKey key = new ConfigDefinitionKey(builder.getDefName(), builder.getDefNamespace());
return new GenericConfig.GenericConfigBuilder(key, payloadBuilder);
}
private ConfigInstance.Builder getConfigInstanceBuilderOverride(ConfigInstance.Builder builder, ConfigPayload payload) {
try {
ConfigTransformer transformer = new ConfigTransformer(builder.getClass().getEnclosingClass());
return transformer.toConfigBuilder(payload);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Error applying override to builder", e);
}
}
/** Returns the one and only HostSystem of the root node. Must be overridden by root node. */
public HostSystem hostSystem() { return getRoot().hostSystem(); }
public AbstractConfigProducerRoot getRoot() {
return parent == null ? null : parent.getRoot();
}
/**
* Returns the {@link ApplicationConfigProducerRoot} that is the parent of this sub-tree, or null
* if this sub-tree has no Vespa parent.
*/
private ApplicationConfigProducerRoot getVespa() {
if (isRoot()) return null;
if (isVespa()) {
return (ApplicationConfigProducerRoot)this;
} else {
return getParent().getVespa();
}
}
private boolean isRoot() {
return parent == null;
}
private boolean isVespa() {
return ((this instanceof ApplicationConfigProducerRoot) && getParent().isRoot());
}
public AnyConfigProducer getParent() { return parent; }
void setupConfigId(String parentConfigId) {
if (this instanceof AbstractConfigProducerRoot) {
configId = "";
} else {
configId = parentConfigId + subId;
addConfigId(configId);
}
computeConfigId();
}
private void computeConfigId() {
if (parent == null) return;
String parentConfigId = parent.getConfigIdPrefix();
if (parentConfigId == null) return;
String oldConfigId = configId;
if (this instanceof AbstractConfigProducerRoot) {
configId = "";
} else {
configId = parentConfigId + subId;
}
if (oldConfigId == null) return;
if (!configId.equals(oldConfigId)) {
throw new IllegalArgumentException("configId cannot change "+oldConfigId+" -> "+configId+" (invalid topology change)");
}
}
protected static ClassLoader findInheritedClassLoader(Class clazz, String producerName) {
Class>[] interfazes = clazz.getInterfaces();
for (Class interfaze : interfazes) {
if (producerName.equals(interfaze.getName())) {
return interfaze.getClassLoader();
}
}
if (clazz.getSuperclass() == null)
return null;
return findInheritedClassLoader(clazz.getSuperclass(), producerName);
}
protected ClassLoader getConfigClassLoader(String producerName) {
return findInheritedClassLoader(getClass(), producerName);
}
public void mergeUserConfigs(UserConfigRepo newRepo) {
userConfigs.merge(newRepo);
}
// TODO: Make producers depend on AdminModel instead
/** Returns a monitoring service if configured, null otherwise */
protected Monitoring getMonitoringService() {
AbstractConfigProducerRoot root = getRoot();
Admin admin = (root == null? null : root.getAdmin());
if (admin == null) {
return null;
}
if (admin.getMonitoring() != null) {
return admin.getMonitoring();
}
return null;
}
// NOPs for all config producers without children; overridden in TreeConfigProducer
void aggregateDescendantServices() { }
public List getDescendantServices() { return List.of(); }
List getChildrenByTypeRecursive(Class type) { return List.of(); }
void freeze() { }
@Override
public void validate() throws Exception { }
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy