All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.yahoo.config.model.producer.TreeConfigProducer Maven / Gradle / Ivy

There is a newer version: 8.409.18
Show newest version
// 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.api.annotations.Beta;
import com.yahoo.config.model.ApplicationConfigProducerRoot;
import com.yahoo.vespa.model.Service;
import com.yahoo.vespa.model.SimpleConfigProducer;
import com.yahoo.vespa.model.utils.FreezableMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * Superclass for all producers with children.
 * Config producers constructs and returns config instances on request.
 *
 * @author gjoranv
 * @author arnej
 */
public abstract class TreeConfigProducer
    extends AnyConfigProducer
{
    private final List descendantServices = new ArrayList<>();
    private final FreezableMap childrenBySubId = new FreezableMap<>(LinkedHashMap.class);

    /**
     * Creates a new TreeConfigProducer 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 TreeConfigProducer(TreeConfigProducer parent, String subId) {
        super(parent, subId);
    }

    /**
     * Create an config producer with a configId only. Used e.g. to create root nodes, and producers
     * that are given children after construction using {@link #addChild(AnyConfigProducer)}.
     *
     * @param subId The sub configId. Note that this can be prefixed when calling addChild with this producer as arg.
     */
    public TreeConfigProducer(String subId) {
        super(subId);
    }

    /**
     * Helper to provide an error message on collisions of sub ids (ignore SimpleConfigProducer, use the parent in that case)
     */
    private String errorMsgClassName() {
        if (getClass().equals(SimpleConfigProducer.class)) return getParent().getClass().getSimpleName();
        return getClass().getSimpleName();
    }

    /**
     * Adds a child to this config producer.
     *
     * @param child the child config producer to add
     */
    protected void addChild(CHILD child) {
        if (child == null) {
            throw new IllegalArgumentException("Trying to add null child for: " + this);
        }
        if (child instanceof AbstractConfigProducerRoot) {
            throw new IllegalArgumentException("Child cannot be a root node: " + child);
        }

        child.setParent(this);
        if (childrenBySubId.get(child.getSubId()) != null) {
            throw new IllegalArgumentException("Multiple services/instances of the id '" + child.getSubId() + "' under the service/instance " +
                                               errorMsgClassName() + " '" + getSubId() + "'. (This is commonly caused by service/node index " +
                                               "collisions in the config.)." +
                                               "\nExisting instance: " + childrenBySubId.get(child.getSubId()) +
                                               "\nAttempted to add:  " + child +
                                               "\nStack trace: " + Arrays.toString(Thread.currentThread().getStackTrace()));
        }
        childrenBySubId.put(child.getSubId(), child);

        if (child instanceof Service) {
            addDescendantService((Service)child);
        }
    }

    public void removeChild(CHILD child) {
        if (child.getParent() != this)
            throw new IllegalArgumentException("Could not remove " + child  + ": Expected its parent to be " +
                                               this + ", but was " + child.getParent());

        if (child instanceof Service)
            descendantServices.remove(child);

        childrenBySubId.remove(child.getSubId());
        child.setParent(null);
    }

    /** Returns this ConfigProducer's children (only 1st level) */
    public Map getChildren() { return Collections.unmodifiableMap(childrenBySubId); }

    @Beta
    public  List getChildrenByTypeRecursive(Class type) {
        List validChildren = new ArrayList<>();

        if (this.getClass().equals(type)) {
            validChildren.add(type.cast(this));
        }

        Map children = this.getChildren();
        for (CHILD child : children.values()) {
            validChildren.addAll(child.getChildrenByTypeRecursive(type));
        }

        return Collections.unmodifiableList(validChildren);
    }

    /** Returns a list of all the children of this who are instances of Service */
    public List getDescendantServices() { return Collections.unmodifiableList(descendantServices); }

    protected void addDescendantService(Service s) { descendantServices.add(s); }

    void setupConfigId(String parentConfigId) {
        super.setupConfigId(parentConfigId);
        setupChildConfigIds(getConfigIdPrefix());
    }

    String getConfigIdPrefix() {
        if (this instanceof AbstractConfigProducerRoot || this instanceof ApplicationConfigProducerRoot) {
            return "";
        }
        if (currentConfigId() == null) {
            return null;
        }
        return getConfigId() + "/";
    }

    @Override
    protected ClassLoader getConfigClassLoader(String producerName) {
        ClassLoader classLoader = findInheritedClassLoader(getClass(), producerName);
        if (classLoader != null)
            return classLoader;

        // TODO: Make logic correct, so that the deepest child will be the one winning.
        for (AnyConfigProducer child : childrenBySubId.values()) {
            ClassLoader loader = child.getConfigClassLoader(producerName);
            if (loader != null) {
                return loader;
            }
        }
        return null;
    }

    private void setupChildConfigIds(String currentConfigId) {
        for (AnyConfigProducer child : childrenBySubId.values()) {
            child.setupConfigId(currentConfigId);
        }
    }

    @Override
    void aggregateDescendantServices() {
        for (CHILD child : childrenBySubId.values()) {
            child.aggregateDescendantServices();
            descendantServices.addAll(child.getDescendantServices());
        }
    }

    @Override
    void freeze() {
        childrenBySubId.freeze();
        for (CHILD child : childrenBySubId.values()) {
            child.freeze();
        }
    }

    @Override
    public void validate() throws Exception {
        assert (childrenBySubId.isFrozen());
        for (CHILD child : childrenBySubId.values()) {
            child.validate();
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy