
com.yahoo.container.di.Container Maven / Gradle / Ivy
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.container.di;
import com.google.inject.Injector;
import com.yahoo.config.ConfigInstance;
import com.yahoo.config.ConfigurationRuntimeException;
import com.yahoo.config.subscription.ConfigInterruptedException;
import com.yahoo.config.subscription.SubscriberClosedException;
import com.yahoo.container.ComponentsConfig;
import com.yahoo.container.bundle.BundleInstantiationSpecification;
import com.yahoo.container.di.ConfigRetriever.BootstrapConfigs;
import com.yahoo.container.di.ConfigRetriever.ComponentsConfigs;
import com.yahoo.container.di.ConfigRetriever.ConfigSnapshot;
import com.yahoo.container.di.componentgraph.core.ComponentGraph;
import com.yahoo.container.di.componentgraph.core.ComponentNode;
import com.yahoo.container.di.componentgraph.core.Node;
import com.yahoo.container.di.config.ApplicationBundlesConfig;
import com.yahoo.container.di.config.PlatformBundlesConfig;
import com.yahoo.container.di.config.SubscriberFactory;
import com.yahoo.vespa.config.ConfigKey;
import com.yahoo.yolean.UncheckedInterruptedException;
import org.osgi.framework.Bundle;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import static java.util.logging.Level.FINE;
/**
* @author gjoranv
* @author Tony Vaagenes
* @author ollivir
*/
public class Container {
private static final Logger log = Logger.getLogger(Container.class.getName());
private final SubscriberFactory subscriberFactory;
private final ConfigKey applicationBundlesConfigKey;
private final ConfigKey platformBundlesConfigKey;
private final ConfigKey componentsConfigKey;
private final ComponentDeconstructor destructor;
private final Osgi osgi;
private final ConfigRetriever retriever;
private List platformBundles; // Used to verify that platform bundles don't change.
private long previousConfigGeneration = -1L;
private long leastGeneration = -1L;
public Container(SubscriberFactory subscriberFactory, String configId, ComponentDeconstructor destructor, Osgi osgi) {
this.subscriberFactory = subscriberFactory;
this.destructor = destructor;
this.osgi = osgi;
applicationBundlesConfigKey = new ConfigKey<>(ApplicationBundlesConfig.class, configId);
platformBundlesConfigKey = new ConfigKey<>(PlatformBundlesConfig.class, configId);
componentsConfigKey = new ConfigKey<>(ComponentsConfig.class, configId);
var bootstrapKeys = Set.of(applicationBundlesConfigKey, platformBundlesConfigKey, componentsConfigKey);
this.retriever = new ConfigRetriever(bootstrapKeys, subscriberFactory);
}
public Container(SubscriberFactory subscriberFactory, String configId, ComponentDeconstructor destructor) {
this(subscriberFactory, configId, destructor, new Osgi() {
});
}
public ComponentGraphResult waitForNextGraphGeneration(ComponentGraph oldGraph, Injector fallbackInjector, boolean isInitializing) {
try {
Collection obsoleteBundles = new HashSet<>();
ComponentGraph newGraph = waitForNewConfigGenAndCreateGraph(oldGraph, fallbackInjector, isInitializing, obsoleteBundles);
newGraph.reuseNodes(oldGraph);
try {
constructComponents(newGraph);
} catch (Throwable e) {
log.log(Level.WARNING, String.format(
"Failed to construct graph for generation '%d' - scheduling partial graph for deconstruction",
newGraph.generation()), e);
deconstructFailedGraph(oldGraph, newGraph);
throw e;
}
Runnable cleanupTask = createPreviousGraphDeconstructionTask(oldGraph, newGraph, obsoleteBundles);
return new ComponentGraphResult(newGraph, cleanupTask);
} catch (Throwable t) {
invalidateGeneration(oldGraph.generation(), t);
throw t;
}
}
private ComponentGraph waitForNewConfigGenAndCreateGraph(
ComponentGraph graph, Injector fallbackInjector, boolean isInitializing, Collection obsoleteBundles) // NOTE: Return value
{
ConfigSnapshot snapshot;
while (true) {
snapshot = retriever.getConfigs(graph.configKeys(), leastGeneration, isInitializing);
if (log.isLoggable(FINE))
log.log(FINE, String.format("getConfigAndCreateGraph:\n" + "graph.configKeys = %s\n" + "graph.generation = %s\n" + "snapshot = %s\n",
graph.configKeys(), graph.generation(), snapshot));
if (snapshot instanceof BootstrapConfigs) {
if (getBootstrapGeneration() <= previousConfigGeneration) {
throw new IllegalStateException(String.format(
"Got bootstrap configs out of sequence for old config generation %d.\n" + "Previous config generation is %d",
getBootstrapGeneration(), previousConfigGeneration));
}
log.log(FINE, () -> "Got new bootstrap generation\n" + configGenerationsString());
if (graph.generation() == 0) {
platformBundles = getConfig(platformBundlesConfigKey, snapshot.configs()).bundlePaths();
osgi.installPlatformBundles(platformBundles);
} else {
throwIfPlatformBundlesChanged(snapshot);
}
Collection bundlesToRemove = installApplicationBundles(snapshot.configs());
obsoleteBundles.addAll(bundlesToRemove);
graph = createComponentsGraph(snapshot.configs(), getBootstrapGeneration(), fallbackInjector);
// Continues loop
} else if (snapshot instanceof ComponentsConfigs) {
break;
}
}
log.log(FINE, () -> "Got components configs,\n" + configGenerationsString());
return createAndConfigureComponentsGraph(snapshot.configs(), fallbackInjector);
}
private long getBootstrapGeneration() {
return retriever.getBootstrapGeneration();
}
private long getComponentsGeneration() {
return retriever.getComponentsGeneration();
}
private String configGenerationsString() {
return String.format("bootstrap generation = %d\n" + "components generation: %d\n" + "previous generation: %d",
getBootstrapGeneration(), getComponentsGeneration(), previousConfigGeneration);
}
private void throwIfPlatformBundlesChanged(ConfigSnapshot snapshot) {
var checkPlatformBundles = getConfig(platformBundlesConfigKey, snapshot.configs()).bundlePaths();
if (! checkPlatformBundles.equals(platformBundles))
throw new RuntimeException("Platform bundles are not allowed to change!\nOld: " + platformBundles + "\nNew: " + checkPlatformBundles);
}
private ComponentGraph createAndConfigureComponentsGraph(Map, ConfigInstance> componentsConfigs,
Injector fallbackInjector) {
ComponentGraph componentGraph = createComponentsGraph(componentsConfigs, getComponentsGeneration(), fallbackInjector);
componentGraph.setAvailableConfigs(componentsConfigs);
return componentGraph;
}
private void constructComponents(ComponentGraph graph) {
graph.nodes().forEach(n -> {
if (Thread.interrupted())
throw new UncheckedInterruptedException("Interrupted while constructing component graph", true);
n.constructInstance();
});
}
private void deconstructFailedGraph(ComponentGraph currentGraph, ComponentGraph failedGraph) {
Set
© 2015 - 2025 Weber Informatics LLC | Privacy Policy