summaryrefslogtreecommitdiffstats
path: root/container-di/src/main/scala
diff options
context:
space:
mode:
Diffstat (limited to 'container-di/src/main/scala')
-rw-r--r--container-di/src/main/scala/com/yahoo/container/bundle/MockBundle.scala94
-rw-r--r--container-di/src/main/scala/com/yahoo/container/di/CloudSubscriberFactory.scala104
-rw-r--r--container-di/src/main/scala/com/yahoo/container/di/ConfigRetriever.scala135
-rw-r--r--container-di/src/main/scala/com/yahoo/container/di/Container.scala265
-rw-r--r--container-di/src/main/scala/com/yahoo/container/di/Osgi.scala40
-rw-r--r--container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/ComponentGraph.scala334
-rw-r--r--container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/ComponentNode.scala213
-rw-r--r--container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/ComponentRegistryNode.scala71
-rw-r--r--container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/GuiceNode.scala41
-rw-r--r--container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/JerseyNode.scala92
-rw-r--r--container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/Node.scala117
-rw-r--r--container-di/src/main/scala/com/yahoo/container/di/osgi/OsgiUtil.scala143
-rw-r--r--container-di/src/main/scala/com/yahoo/container/di/package.scala44
13 files changed, 0 insertions, 1693 deletions
diff --git a/container-di/src/main/scala/com/yahoo/container/bundle/MockBundle.scala b/container-di/src/main/scala/com/yahoo/container/bundle/MockBundle.scala
deleted file mode 100644
index 5a28d9abe2a..00000000000
--- a/container-di/src/main/scala/com/yahoo/container/bundle/MockBundle.scala
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.container.bundle
-
-
-import java.net.URL
-import java.util
-
-import org.osgi.framework.wiring._
-import org.osgi.framework.{ServiceReference, Version, Bundle}
-import java.io.InputStream
-import java.util.{Collections, Hashtable, Dictionary}
-import MockBundle._
-import org.osgi.resource.{Capability, Wire, Requirement}
-
-/**
- * @author gjoranv
- */
-class MockBundle extends Bundle with BundleWiring {
-
- override def getState = Bundle.ACTIVE
-
- override def start(options: Int) {}
- override def start() {}
- override def stop(options: Int) {}
- override def stop() {}
- override def update(input: InputStream) {}
- override def update() {}
- override def uninstall() {}
-
- override def getHeaders(locale: String) = getHeaders
-
- override def getSymbolicName = SymbolicName
- override def getVersion: Version = BundleVersion
- override def getLocation = getSymbolicName
- override def getBundleId: Long = 0L
-
- override def getHeaders: Dictionary[String, String] = new Hashtable[String, String]()
-
- override def getRegisteredServices = Array[ServiceReference[_]]()
- override def getServicesInUse = getRegisteredServices
-
- override def hasPermission(permission: Any) = true
-
- override def getResource(name: String) = throw new UnsupportedOperationException
- override def loadClass(name: String) = throw new UnsupportedOperationException
- override def getResources(name: String) = throw new UnsupportedOperationException
-
- override def getEntryPaths(path: String) = throw new UnsupportedOperationException
- override def getEntry(path: String) = throw new UnsupportedOperationException
- override def findEntries(path: String, filePattern: String, recurse: Boolean) = Collections.emptyEnumeration()
-
-
- override def getLastModified = 1L
-
- override def getBundleContext = throw new UnsupportedOperationException
- override def getSignerCertificates(signersType: Int) = Collections.emptyMap()
-
- override def adapt[A](`type`: Class[A]) =
- `type` match {
- case MockBundle.bundleWiringClass => this.asInstanceOf[A]
- case _ => ???
- }
-
- override def getDataFile(filename: String) = null
- override def compareTo(o: Bundle) = getBundleId compareTo o.getBundleId
-
-
- //TODO: replace with mockito
- override def findEntries(p1: String, p2: String, p3: Int): util.List[URL] = ???
- override def getRequiredResourceWires(p1: String): util.List[Wire] = ???
- override def getResourceCapabilities(p1: String): util.List[Capability] = ???
- override def isCurrent: Boolean = ???
- override def getRequiredWires(p1: String): util.List[BundleWire] = ???
- override def getCapabilities(p1: String): util.List[BundleCapability] = ???
- override def getProvidedResourceWires(p1: String): util.List[Wire] = ???
- override def getProvidedWires(p1: String): util.List[BundleWire] = ???
- override def getRevision: BundleRevision = ???
- override def getResourceRequirements(p1: String): util.List[Requirement] = ???
- override def isInUse: Boolean = ???
- override def listResources(p1: String, p2: String, p3: Int): util.Collection[String] = Collections.emptyList()
- override def getClassLoader: ClassLoader = MockBundle.getClass.getClassLoader
- override def getRequirements(p1: String): util.List[BundleRequirement] = ???
- override def getResource: BundleRevision = ???
- override def getBundle: Bundle = ???
-}
-
-object MockBundle {
- val SymbolicName = "mock-bundle"
- val BundleVersion = new Version(1, 0, 0)
-
- val bundleWiringClass = classOf[BundleWiring]
-
-
-}
diff --git a/container-di/src/main/scala/com/yahoo/container/di/CloudSubscriberFactory.scala b/container-di/src/main/scala/com/yahoo/container/di/CloudSubscriberFactory.scala
deleted file mode 100644
index 0f3fab93e80..00000000000
--- a/container-di/src/main/scala/com/yahoo/container/di/CloudSubscriberFactory.scala
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.container.di
-
-import java.util.logging.{Level, Logger}
-
-import com.yahoo.config.ConfigInstance
-import com.yahoo.config.subscription.{ConfigHandle, ConfigSource, ConfigSourceSet, ConfigSubscriber}
-import com.yahoo.container.di.CloudSubscriberFactory._
-import com.yahoo.container.di.config.{Subscriber, SubscriberFactory}
-import com.yahoo.vespa.config.ConfigKey
-
-import scala.collection.JavaConverters._
-import scala.language.existentials
-
-
-/**
- * @author Tony Vaagenes
- */
-class CloudSubscriberFactory(configSource: ConfigSource) extends SubscriberFactory {
-
- private var testGeneration: Option[Long] = None
-
- private val activeSubscribers = new java.util.WeakHashMap[CloudSubscriber, Int]()
-
- override def getSubscriber(configKeys: java.util.Set[_ <: ConfigKey[_]]): Subscriber = {
- val subscriber = new CloudSubscriber(configKeys.asScala.toSet.asInstanceOf[Set[ConfigKeyT]], configSource)
-
- testGeneration.foreach(subscriber.subscriber.reload(_)) //TODO: test specific code, remove
- activeSubscribers.put(subscriber, 0)
-
- subscriber
- }
-
- //TODO: test specific code, remove
- override def reloadActiveSubscribers(generation: Long) {
- testGeneration = Some(generation)
-
- val l = activeSubscribers.keySet().asScala.toSet
- l.foreach { _.subscriber.reload(generation) }
- }
-}
-
-object CloudSubscriberFactory {
- val log = Logger.getLogger(classOf[CloudSubscriberFactory].getName)
-
- private class CloudSubscriber(keys: Set[ConfigKeyT], configSource: ConfigSource) extends Subscriber
- {
- private[CloudSubscriberFactory] val subscriber = new ConfigSubscriber(configSource)
- private val handles: Map[ConfigKeyT, ConfigHandle[_ <: ConfigInstance]] = keys.map(subscribe).toMap
-
-
- // if waitNextGeneration has not yet been called, -1 should be returned
- var generation: Long = -1
-
- // True if this reconfiguration was caused by a system-internal redeploy, not an external application change
- var internalRedeploy: Boolean = false
-
- private def subscribe(key: ConfigKeyT) = (key, subscriber.subscribe(key.getConfigClass, key.getConfigId))
-
- override def configChanged = handles.values.exists(_.isChanged)
-
- //mapValues returns a view,, so we need to force evaluation of it here to prevent deferred evaluation.
- override def config = handles.mapValues(_.getConfig).toMap.view.force.
- asInstanceOf[Map[ConfigKey[ConfigInstance], ConfigInstance]].asJava
-
- override def waitNextGeneration() = {
- require(!handles.isEmpty)
-
- /* Catch and just log config exceptions due to missing config values for parameters that do
- * not have a default value. These exceptions occur when the user has removed a component
- * from services.xml, and the component takes a config that has parameters without a
- * default value in the def-file. There is a new 'components' config underway, where the
- * component is removed, so this old config generation will soon be replaced by a new one. */
- var gotNextGen = false
- var numExceptions = 0
- while (!gotNextGen) {
- try{
- if (subscriber.nextGeneration())
- gotNextGen = true
- } catch {
- case e: IllegalArgumentException =>
- numExceptions += 1
- log.log(Level.WARNING, "Got exception from the config system (please ignore the exception if you just removed "
- + "a component from your application that used the mentioned config): ", e)
- if (numExceptions >= 5)
- throw new IllegalArgumentException("Failed retrieving the next config generation.", e)
- }
- }
-
- generation = subscriber.getGeneration
- internalRedeploy = subscriber.isInternalRedeploy
- generation
- }
-
- override def close() {
- subscriber.close()
- }
- }
-
-
- class Provider extends com.google.inject.Provider[SubscriberFactory] {
- override def get() = new CloudSubscriberFactory(ConfigSourceSet.createDefault())
- }
-}
diff --git a/container-di/src/main/scala/com/yahoo/container/di/ConfigRetriever.scala b/container-di/src/main/scala/com/yahoo/container/di/ConfigRetriever.scala
deleted file mode 100644
index aad9e17acb2..00000000000
--- a/container-di/src/main/scala/com/yahoo/container/di/ConfigRetriever.scala
+++ /dev/null
@@ -1,135 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.container.di
-
-
-import java.util.logging.{Level, Logger}
-
-import com.yahoo.config.ConfigInstance
-import com.yahoo.container.di.ConfigRetriever._
-import com.yahoo.container.di.config.Subscriber
-import com.yahoo.log.LogLevel.DEBUG
-
-import scala.annotation.tailrec
-import scala.collection.JavaConverters._
-import scala.language.postfixOps
-
-/**
- * @author Tony Vaagenes
- * @author gjoranv
- */
-final class ConfigRetriever(bootstrapKeys: Set[ConfigKeyT],
- subscribe: Set[ConfigKeyT] => Subscriber)
-{
- require(!bootstrapKeys.isEmpty)
-
- private val bootstrapSubscriber: Subscriber = subscribe(bootstrapKeys)
-
- private var componentSubscriber: Subscriber = subscribe(Set())
- private var componentSubscriberKeys: Set[ConfigKeyT] = Set()
-
- /** Loop forever until we get config */
- @tailrec
- final def getConfigs(componentConfigKeys: Set[ConfigKeyT], leastGeneration: Long, restartOnRedeploy: Boolean = false): ConfigSnapshot = {
- require(componentConfigKeys intersect bootstrapKeys isEmpty)
- log.log(DEBUG, "getConfigs: " + componentConfigKeys)
-
- setupComponentSubscriber(componentConfigKeys ++ bootstrapKeys)
-
- getConfigsOptional(leastGeneration, restartOnRedeploy) match {
- case Some(snapshot) => resetComponentSubscriberIfBootstrap(snapshot); snapshot
- case None => getConfigs(componentConfigKeys, leastGeneration, restartOnRedeploy)
- }
- }
-
-
- /** Try to get config just once */
- final def getConfigsOnce(componentConfigKeys: Set[ConfigKeyT], leastGeneration: Long, restartOnRedeploy: Boolean = false): Option[ConfigSnapshot] = {
- require(componentConfigKeys intersect bootstrapKeys isEmpty)
- log.log(DEBUG, "getConfigsOnce: " + componentConfigKeys)
-
- setupComponentSubscriber(componentConfigKeys ++ bootstrapKeys)
-
- getConfigsOptional(leastGeneration, restartOnRedeploy) match {
- case Some(snapshot) => resetComponentSubscriberIfBootstrap(snapshot); Some(snapshot)
- case None => None;
- }
- }
-
- private def getConfigsOptional(leastGeneration: Long, restartOnRedeploy: Boolean): Option[ConfigSnapshot] = {
- val newestComponentGeneration = componentSubscriber.waitNextGeneration()
- log.log(DEBUG, s"getConfigsOptional: new component generation: $newestComponentGeneration")
-
- // leastGeneration is only used to ensure newer generation when the previous generation was invalidated due to an exception
- if (newestComponentGeneration < leastGeneration) {
- None
- } else if (restartOnRedeploy && ! componentSubscriber.internalRedeploy()) { // Don't reconfig - wait for restart
- None
- } else if (bootstrapSubscriber.generation < newestComponentGeneration) {
- val newestBootstrapGeneration = bootstrapSubscriber.waitNextGeneration()
- log.log(DEBUG, s"getConfigsOptional: new bootstrap generation: ${bootstrapSubscriber.generation}")
- bootstrapConfigIfChanged() orElse {
- if (newestBootstrapGeneration == newestComponentGeneration){
- log.log(DEBUG, s"Got new components configs with unchanged bootstrap configs.")
- componentsConfigIfChanged()
- } else {
- // This should not be a normal case, and hence a warning to allow investigation.
- log.warning(s"Did not get same generation for bootstrap ($newestBootstrapGeneration) and components configs ($newestComponentGeneration).")
- None
- }
- }
- } else {
- // bootstrapGen==componentGen (happens only when a new component subscriber returns first config after bootstrap)
- componentsConfigIfChanged()
- }
- }
-
- private def bootstrapConfigIfChanged(): Option[BootstrapConfigs] = configIfChanged(bootstrapSubscriber, BootstrapConfigs)
- private def componentsConfigIfChanged(): Option[ComponentsConfigs] = configIfChanged(componentSubscriber, ComponentsConfigs)
-
- private def configIfChanged[T <: ConfigSnapshot](subscriber: Subscriber,
- constructor: Map[ConfigKeyT, ConfigInstance] => T ): Option[T] = {
- if (subscriber.configChanged) Some(constructor(subscriber.config.asScala.toMap))
- else None
- }
-
- private def resetComponentSubscriberIfBootstrap(snapshot: ConfigSnapshot) {
- snapshot match {
- case BootstrapConfigs(_) => setupComponentSubscriber(Set())
- case _ =>
- }
- }
-
- private def setupComponentSubscriber(keys: Set[ConfigKeyT]) {
- if (componentSubscriberKeys != keys) {
- componentSubscriber.close()
- componentSubscriberKeys = keys
- try {
- log.log(DEBUG, s"Setting up new component subscriber for keys: $keys")
- componentSubscriber = subscribe(keys)
- } catch {
- case e: Throwable =>
- log.log(Level.WARNING, s"Failed setting up subscriptions for component configs: ${e.getMessage}")
- log.log(Level.WARNING, s"Config keys: $keys")
- throw e
- }
- }
- }
-
- def shutdown() {
- bootstrapSubscriber.close()
- componentSubscriber.close()
- }
-
- //TODO: check if these are really needed
- final def getBootstrapGeneration = bootstrapSubscriber.generation
- final def getComponentsGeneration = componentSubscriber.generation
-}
-
-
-object ConfigRetriever {
- private val log = Logger.getLogger(classOf[ConfigRetriever].getName)
-
- sealed abstract class ConfigSnapshot
- case class BootstrapConfigs(configs: Map[ConfigKeyT, ConfigInstance]) extends ConfigSnapshot
- case class ComponentsConfigs(configs: Map[ConfigKeyT, ConfigInstance]) extends ConfigSnapshot
-}
diff --git a/container-di/src/main/scala/com/yahoo/container/di/Container.scala b/container-di/src/main/scala/com/yahoo/container/di/Container.scala
deleted file mode 100644
index 2a185d41a6c..00000000000
--- a/container-di/src/main/scala/com/yahoo/container/di/Container.scala
+++ /dev/null
@@ -1,265 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.container.di
-
-import java.util.logging.{Level, Logger}
-import java.util.{IdentityHashMap, Random}
-
-import com.google.inject.{Guice, Injector}
-import com.yahoo.config._
-import com.yahoo.config.subscription.ConfigInterruptedException
-import com.yahoo.container.bundle.BundleInstantiationSpecification
-import com.yahoo.container.di.ConfigRetriever.{BootstrapConfigs, ComponentsConfigs}
-import com.yahoo.container.di.Container._
-import com.yahoo.container.di.componentgraph.core.ComponentNode.ComponentConstructorException
-import com.yahoo.container.di.componentgraph.core.{ComponentGraph, ComponentNode, JerseyNode}
-import com.yahoo.container.di.config.{RestApiContext, SubscriberFactory}
-import com.yahoo.container.{BundlesConfig, ComponentsConfig}
-import com.yahoo.log.LogLevel.DEBUG
-import com.yahoo.protect.Process
-import com.yahoo.vespa.config.ConfigKey
-
-import scala.collection.JavaConverters._
-import scala.concurrent.duration._
-import scala.language.postfixOps
-import scala.math.max
-
-
-/**
- *
- * @author gjoranv
- * @author Tony Vaagenes
- */
-class Container(
- subscriberFactory: SubscriberFactory,
- configId: String,
- componentDeconstructor: ComponentDeconstructor,
- osgi: Osgi = new Osgi {}
- )
-{
- val bundlesConfigKey = new ConfigKey(classOf[BundlesConfig], configId)
- val componentsConfigKey = new ConfigKey(classOf[ComponentsConfig], configId)
-
- var configurer = new ConfigRetriever(Set(bundlesConfigKey, componentsConfigKey), (keys) => subscriberFactory.getSubscriber(keys.asJava))
- var previousConfigGeneration = -1L
- var leastGeneration = -1L
-
- @throws(classOf[InterruptedException])
- def getNewComponentGraph(oldGraph: ComponentGraph = new ComponentGraph,
- fallbackInjector: GuiceInjector = Guice.createInjector(),
- restartOnRedeploy: Boolean = false): ComponentGraph = {
-
- def deconstructObsoleteComponents(oldGraph: ComponentGraph, newGraph: ComponentGraph) {
- val oldComponents = new IdentityHashMap[AnyRef, AnyRef]()
- oldGraph.allComponentsAndProviders foreach (oldComponents.put(_, null))
- newGraph.allComponentsAndProviders foreach (oldComponents.remove(_))
- oldComponents.keySet.asScala foreach (componentDeconstructor.deconstruct(_))
- }
-
- try {
- val newGraph = getConfigAndCreateGraph(oldGraph, fallbackInjector, restartOnRedeploy)
- newGraph.reuseNodes(oldGraph)
- constructComponents(newGraph)
- deconstructObsoleteComponents(oldGraph, newGraph)
- newGraph
- } catch {
- case userException: ComponentConstructorException =>
- invalidateGeneration(oldGraph.generation, userException)
- // TODO: Wrap userException in an Error when generation==0 (+ unit test that Error is thrown)
- throw userException
- case t: Throwable =>
- invalidateGeneration(oldGraph.generation, t)
- throw t
- }
- }
-
- private def invalidateGeneration(generation: Long, cause: Throwable) {
- val maxWaitToExit = 60 seconds
-
- def newGraphErrorMessage(generation: Long, cause: Throwable): String = {
- val failedFirstMessage = "Failed to set up first component graph"
- val failedNewMessage = "Failed to set up new component graph"
- val constructMessage = "due to error when constructing one of the components"
- val exitMessage = s"Exiting within $maxWaitToExit."
- val retainMessage = "Retaining previous component generation."
- generation match {
- case 0 =>
- cause match {
- case _: ComponentConstructorException => s"$failedFirstMessage $constructMessage. $exitMessage"
- case _ => s"$failedFirstMessage. $exitMessage"
- }
- case _ =>
- cause match {
- case _: ComponentConstructorException => s"$failedNewMessage $constructMessage. $retainMessage"
- case _ => s"$failedNewMessage. $retainMessage"
- }
- }
- }
-
- // TODO: move to ConfiguredApplication
- def logAndDie(message: String, cause: Throwable): Unit = {
- log.log(Level.SEVERE, message, cause)
- try {
- Thread.sleep((new Random(System.nanoTime).nextDouble * maxWaitToExit.toMillis).toLong)
- } catch {
- case _: InterruptedException => // Do nothing
- }
- Process.logAndDie("Exited for reason (repeated from above):", cause)
- }
-
- leastGeneration = max(configurer.getComponentsGeneration, configurer.getBootstrapGeneration) + 1
- cause match {
- case _: InterruptedException | _: ConfigInterruptedException => // Normal during shutdown, do not log anything.
- case _ => log.log(Level.WARNING, newGraphErrorMessage(generation, cause), cause)
- }
- }
-
- final def getConfigAndCreateGraph(graph: ComponentGraph = new ComponentGraph,
- fallbackInjector: Injector,
- restartOnRedeploy: Boolean): ComponentGraph = {
-
- val snapshot = configurer.getConfigs(graph.configKeys, leastGeneration, restartOnRedeploy)
-
- log.log(DEBUG,
- """createNewGraph:
- |graph.configKeys = %s
- |graph.generation = %s
- |snapshot = %s"""
- .format(graph.configKeys, graph.generation, snapshot).stripMargin)
-
- val preventTailRecursion =
- snapshot match {
- case BootstrapConfigs(configs) =>
- // TODO: remove require when proven unnecessary
- require(getBootstrapGeneration > previousConfigGeneration,
- """Got bootstrap configs out of sequence for old config generation %d.
- |Previous config generation is %d""".format(getBootstrapGeneration, previousConfigGeneration))
- log.log(DEBUG,
- """Got new bootstrap generation
- |bootstrap generation = %d
- |components generation: %d
- |previous generation: %d"""
- .format(getBootstrapGeneration, getComponentsGeneration, previousConfigGeneration).stripMargin)
- installBundles(configs)
- getConfigAndCreateGraph(
- createComponentsGraph(configs, getBootstrapGeneration,fallbackInjector), fallbackInjector, restartOnRedeploy)
- case ComponentsConfigs(configs) =>
- log.log(DEBUG,
- """Got components configs,
- |bootstrap generation = %d
- |components generation: %d
- |previous generation: %d"""
- .format(getBootstrapGeneration, getComponentsGeneration, previousConfigGeneration).stripMargin)
- createAndConfigureComponentsGraph(configs, fallbackInjector)
- }
-
- preventTailRecursion
- }
-
-
- def getBootstrapGeneration: Long = {
- configurer.getBootstrapGeneration
- }
-
- def getComponentsGeneration: Long = {
- configurer.getComponentsGeneration
- }
-
- private def createAndConfigureComponentsGraph[T](componentsConfigs: Map[ConfigKeyT, ConfigInstance],
- fallbackInjector: Injector): ComponentGraph = {
-
- val componentGraph = createComponentsGraph(componentsConfigs, getComponentsGeneration, fallbackInjector)
- componentGraph.setAvailableConfigs(componentsConfigs)
- componentGraph
- }
-
- def injectNodes(config: ComponentsConfig, graph: ComponentGraph) {
- for {
- component <- config.components().asScala
- inject <- component.inject().asScala
- } {
- def getNode = ComponentGraph.getNode(graph, _: String)
-
- //TODO: Support inject.name()
- getNode(component.id()).inject(getNode(inject.id()))
- }
-
- }
-
- def installBundles(configsIncludingBootstrapConfigs: Map[ConfigKeyT, ConfigInstance]) {
- val bundlesConfig = getConfig(bundlesConfigKey, configsIncludingBootstrapConfigs)
- osgi.useBundles(bundlesConfig.bundle())
- }
-
- private def createComponentsGraph[T](
- configsIncludingBootstrapConfigs: Map[ConfigKeyT, ConfigInstance],
- generation: Long,
- fallbackInjector: Injector): ComponentGraph = {
-
- previousConfigGeneration = generation
-
- val graph = new ComponentGraph(generation)
-
- val componentsConfig = getConfig(componentsConfigKey, configsIncludingBootstrapConfigs)
- if (componentsConfig == null)
- throw new ConfigurationRuntimeException(
- "The set of all configs does not include a valid 'components' config. Config set: " + configsIncludingBootstrapConfigs.keySet)
- addNodes(componentsConfig, graph)
- injectNodes(componentsConfig, graph)
-
- graph.complete(fallbackInjector)
- graph
- }
-
- def addNodes[T](componentsConfig: ComponentsConfig, graph: ComponentGraph) {
- def isRestApiContext(clazz: Class[_]) = classOf[RestApiContext].isAssignableFrom(clazz)
- def asRestApiContext(clazz: Class[_]) = clazz.asInstanceOf[Class[RestApiContext]]
-
- for (config : ComponentsConfig.Components <- componentsConfig.components.asScala) {
- val specification = bundleInstatiationSpecification(config)
- val componentClass = osgi.resolveClass(specification)
-
- val componentNode =
- if (isRestApiContext(componentClass))
- new JerseyNode(specification.id, config.configId(), asRestApiContext(componentClass), osgi)
- else
- new ComponentNode(specification.id, config.configId(), componentClass)
-
- graph.add(componentNode)
- }
- }
-
- private def constructComponents(graph: ComponentGraph) {
- graph.nodes foreach (_.newOrCachedInstance())
- }
-
- def shutdown(graph: ComponentGraph, deconstructor: ComponentDeconstructor) {
- shutdownConfigurer()
- if (graph != null)
- deconstructAllComponents(graph, deconstructor)
- }
-
- def shutdownConfigurer() {
- configurer.shutdown()
- }
-
- // Reload config manually, when subscribing to non-configserver sources
- def reloadConfig(generation: Long) {
- subscriberFactory.reloadActiveSubscribers(generation)
- }
-
- def deconstructAllComponents(graph: ComponentGraph, deconstructor: ComponentDeconstructor) {
- graph.allComponentsAndProviders foreach(deconstructor.deconstruct(_))
- }
-
-}
-
-object Container {
- val log = Logger.getLogger(classOf[Container].getName)
-
- def getConfig[T <: ConfigInstance](key: ConfigKey[T], configs: Map[ConfigKeyT, ConfigInstance]) : T = {
- key.getConfigClass.cast(configs.getOrElse(key.asInstanceOf[ConfigKeyT], sys.error("Missing config " + key)))
- }
-
- def bundleInstatiationSpecification(config: ComponentsConfig.Components) =
- BundleInstantiationSpecification.getFromStrings(config.id(), config.classId(), config.bundle())
-}
diff --git a/container-di/src/main/scala/com/yahoo/container/di/Osgi.scala b/container-di/src/main/scala/com/yahoo/container/di/Osgi.scala
deleted file mode 100644
index 3407eceae3e..00000000000
--- a/container-di/src/main/scala/com/yahoo/container/di/Osgi.scala
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.container.di
-
-import com.yahoo.config.FileReference
-import com.yahoo.container.bundle.{MockBundle, BundleInstantiationSpecification}
-import com.yahoo.container.di.Osgi.BundleClasses
-import org.osgi.framework.Bundle
-import com.yahoo.component.ComponentSpecification
-
-/**
- *
- * @author gjoranv
- * @author Tony Vaagenes
- */
-trait Osgi {
-
- def getBundleClasses(bundle: ComponentSpecification, packagesToScan: Set[String]): BundleClasses = {
- BundleClasses(new MockBundle, List())
- }
-
- def useBundles(bundles: java.util.Collection[FileReference]) {
- println("useBundles " + bundles.toArray.mkString(", "))
- }
-
- def resolveClass(spec: BundleInstantiationSpecification): Class[AnyRef] = {
- println("resolving class " + spec.classId)
- Class.forName(spec.classId.getName).asInstanceOf[Class[AnyRef]]
- }
-
- def getBundle(spec: ComponentSpecification): Bundle = {
- println("resolving bundle " + spec)
- new MockBundle()
- }
-
-}
-
-object Osgi {
- type RelativePath = String //e.g. "com/yahoo/MyClass.class"
- case class BundleClasses(bundle: Bundle, classEntries: Iterable[RelativePath])
-}
diff --git a/container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/ComponentGraph.scala b/container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/ComponentGraph.scala
deleted file mode 100644
index 92f489798c9..00000000000
--- a/container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/ComponentGraph.scala
+++ /dev/null
@@ -1,334 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.container.di.componentgraph.core
-
-import java.util.logging.Logger
-
-import com.yahoo.component.provider.ComponentRegistry
-import com.yahoo.config.ConfigInstance
-
-import java.lang.annotation.{Annotation => JavaAnnotation}
-import java.lang.IllegalStateException
-import com.yahoo.log.LogLevel
-
-import collection.mutable
-import annotation.tailrec
-
-import com.yahoo.container.di.{ConfigKeyT, GuiceInjector}
-import com.yahoo.container.di.componentgraph.Provider
-import com.yahoo.vespa.config.ConfigKey
-import com.google.inject.{Guice, ConfigurationException, Key, BindingAnnotation}
-import net.jcip.annotations.NotThreadSafe
-
-import com.yahoo.component.{AbstractComponent, ComponentId}
-import java.lang.reflect.{TypeVariable, WildcardType, Method, ParameterizedType, Type}
-import com.yahoo.container.di.removeStackTrace
-import scala.util.Try
-import scala.Some
-
-import scala.language.existentials
-
-/**
- * @author Tony Vaagenes
- * @author gjoranv
- */
-@NotThreadSafe
-class ComponentGraph(val generation: Long = 0) {
-
- import ComponentGraph._
-
- private var nodesById = Map[ComponentId, Node]()
-
- private[di] def size = nodesById.size
-
- def nodes = nodesById.values
-
- def add(component: Node) {
- require(!nodesById.isDefinedAt(component.componentId), "Multiple components with the same id " + component.componentId)
- nodesById += component.componentId -> component
- }
-
- def lookupGlobalComponent(key: Key[_]): Option[Node] = {
- require(key.getTypeLiteral.getType.isInstanceOf[Class[_]], "Type not supported " + key.getTypeLiteral)
- val clazz = key.getTypeLiteral.getRawType
-
-
- val components = matchingComponentNodes(nodesById.values, key)
-
- def singleNonProviderComponentOrThrow: Option[Node] = {
- val nonProviderComponents = components filter (c => !classOf[Provider[_]].isAssignableFrom(c.instanceType))
- nonProviderComponents.size match {
- case 0 => throw new IllegalStateException(s"Multiple global component providers for class '${clazz.getName}' found")
- case 1 => Some(nonProviderComponents.head.asInstanceOf[Node])
- case _ => throw new IllegalStateException(s"Multiple global components with class '${clazz.getName}' found")
- }
- }
-
- components.size match {
- case 0 => None
- case 1 => Some(components.head.asInstanceOf[Node])
- case _ => singleNonProviderComponentOrThrow
- }
- }
-
- def getInstance[T](clazz: Class[T]) : T = {
- getInstance(Key.get(clazz))
- }
-
- def getInstance[T](key: Key[T]) : T = {
- lookupGlobalComponent(key).
- // TODO: Combine exception handling with lookupGlobalComponent.
- getOrElse(throw new IllegalStateException("No global component with key '%s' ".format(key.toString))).
- newOrCachedInstance().asInstanceOf[T]
- }
-
- private def componentNodes: Traversable[ComponentNode] =
- nodesOfType(nodesById.values, classOf[ComponentNode])
-
- private def componentRegistryNodes: Traversable[ComponentRegistryNode] =
- nodesOfType(nodesById.values, classOf[ComponentRegistryNode])
-
- private def osgiComponentsOfClass(clazz: Class[_]): Traversable[ComponentNode] = {
- componentNodes.filter(node => clazz.isAssignableFrom(node.componentType))
- }
-
- def complete(fallbackInjector: GuiceInjector = Guice.createInjector()) {
- def detectCycles = topologicalSort(nodesById.values.toList)
-
- componentNodes foreach {completeNode(_, fallbackInjector)}
- componentRegistryNodes foreach completeComponentRegistryNode
- detectCycles
- }
-
- def configKeys: Set[ConfigKeyT] = {
- nodesById.values.flatMap(_.configKeys).toSet
- }
-
- def setAvailableConfigs(configs: Map[ConfigKeyT, ConfigInstance]) {
- componentNodes foreach { _.setAvailableConfigs(configs) }
- }
-
- def reuseNodes(old: ComponentGraph) {
- def copyInstancesIfNodeEqual() {
- val commonComponentIds = nodesById.keySet & old.nodesById.keySet
- for (id <- commonComponentIds) {
- if (nodesById(id) == old.nodesById(id)) {
- nodesById(id).instance = old.nodesById(id).instance
- }
- }
- }
- def resetInstancesWithModifiedDependencies() {
- for {
- node <- topologicalSort(nodesById.values.toList)
- usedComponent <- node.usedComponents
- } {
- if (usedComponent.instance == None) {
- node.instance = None
- }
- }
- }
-
- copyInstancesIfNodeEqual()
- resetInstancesWithModifiedDependencies()
- }
-
- def allComponentsAndProviders = nodes map {_.instance.get}
-
- private def completeComponentRegistryNode(registry: ComponentRegistryNode) {
- registry.injectAll(osgiComponentsOfClass(registry.componentClass))
- }
-
- private def completeNode(node: ComponentNode, fallbackInjector: GuiceInjector) {
- try {
- val arguments = node.getAnnotatedConstructorParams.map(handleParameter(node, fallbackInjector, _))
-
- node.setArguments(arguments)
- } catch {
- case e : Exception => throw removeStackTrace(new RuntimeException(s"When resolving dependencies of ${node.idAndType}", e))
- }
- }
-
- private def handleParameter(node : Node,
- fallbackInjector: GuiceInjector,
- annotatedParameterType: (Type, Array[JavaAnnotation])): AnyRef =
- {
- def isConfigParameter(clazz : Class[_]) = classOf[ConfigInstance].isAssignableFrom(clazz)
- def isComponentRegistry(t : Type) = t == classOf[ComponentRegistry[_]]
-
- val (parameterType, annotations) = annotatedParameterType
-
- (parameterType match {
- case componentIdClass: Class[_] if componentIdClass == classOf[ComponentId] => node.componentId
- case configClass : Class[_] if isConfigParameter(configClass) => handleConfigParameter(node.asInstanceOf[ComponentNode], configClass)
- case registry : ParameterizedType if isComponentRegistry(registry.getRawType) => getComponentRegistry(registry.getActualTypeArguments.head)
- case clazz : Class[_] => handleComponentParameter(node, fallbackInjector, clazz, annotations)
- case other: ParameterizedType => sys.error(s"Injection of parameterized type $other is not supported.")
- case other => sys.error(s"Injection of type $other is not supported.")
- }).asInstanceOf[AnyRef]
- }
-
-
- def newComponentRegistryNode(componentClass: Class[AnyRef]): ComponentRegistryNode = {
- val registry = new ComponentRegistryNode(componentClass)
- add(registry) //TODO: don't mutate nodes here.
- registry
- }
-
- private def getComponentRegistry(componentType : Type) : ComponentRegistryNode = {
- val componentClass = componentType match {
- case wildCardType: WildcardType =>
- assert(wildCardType.getLowerBounds.isEmpty)
- assert(wildCardType.getUpperBounds.size == 1)
- wildCardType.getUpperBounds.head.asInstanceOf[Class[AnyRef]]
- case clazz: Class[_] => clazz
- case typeVariable: TypeVariable[_] =>
- throw new RuntimeException("Can't create ComponentRegistry of unknown type variable " + typeVariable)
- }
-
- componentRegistryNodes.find(_.componentClass == componentType).
- getOrElse(newComponentRegistryNode(componentClass.asInstanceOf[Class[AnyRef]]))
- }
-
- def handleConfigParameter(node : ComponentNode, clazz: Class[_]) : ConfigKeyT = {
- new ConfigKey(clazz.asInstanceOf[Class[ConfigInstance]], node.configId)
- }
-
- def getKey(clazz: Class[_], bindingAnnotation: Option[JavaAnnotation]) =
- bindingAnnotation.map(Key.get(clazz, _)).getOrElse(Key.get(clazz))
-
- private def handleComponentParameter(node: Node,
- fallbackInjector: GuiceInjector,
- clazz: Class[_],
- annotations: Array[JavaAnnotation]) : Node = {
-
- val bindingAnnotations = annotations.filter(isBindingAnnotation)
- val key = getKey(clazz, bindingAnnotations.headOption)
-
- def matchingGuiceNode(key: Key[_], instance: AnyRef): Option[GuiceNode] = {
- matchingNodes(nodesById.values, classOf[GuiceNode], key).
- filter(node => node.newOrCachedInstance eq instance). // TODO: assert that there is only one (after filter)
- headOption
- }
-
- def lookupOrCreateGlobalComponent: Node = {
- lookupGlobalComponent(key).getOrElse {
- val instance =
- try {
- log.log(LogLevel.DEBUG, "Trying the fallback injector to create" + messageForNoGlobalComponent(clazz, node))
- fallbackInjector.getInstance(key).asInstanceOf[AnyRef]
- } catch {
- case e: ConfigurationException =>
- throw removeStackTrace(new IllegalStateException(
- if (messageForMultipleClassLoaders(clazz).isEmpty)
- "No global" + messageForNoGlobalComponent(clazz, node)
- else
- messageForMultipleClassLoaders(clazz)))
-
- }
- matchingGuiceNode(key, instance).getOrElse {
- val node = new GuiceNode(instance, key.getAnnotation)
- add(node)
- node
- }
- }
- }
-
- if (bindingAnnotations.size > 1)
- sys.error("More than one binding annotation used in class '%s'".format(node.instanceType))
-
- val injectedNodesOfCorrectType = matchingComponentNodes(node.componentsToInject, key)
- injectedNodesOfCorrectType.size match {
- case 0 => lookupOrCreateGlobalComponent
- case 1 => injectedNodesOfCorrectType.head.asInstanceOf[Node]
- case _ => sys.error("Multiple components of type '%s' injected into component '%s'".format(clazz.getName, node.instanceType)) //TODO: !className for last parameter
- }
- }
-
-}
-
-object ComponentGraph {
- val log = Logger.getLogger(classOf[ComponentGraph].getName)
-
- def messageForNoGlobalComponent(clazz: Class[_], node: Node) =
- s" component of class ${clazz.getName} to inject into component ${node.idAndType}."
-
- def messageForMultipleClassLoaders(clazz: Class[_]): String = {
- val errMsg = "Class " + clazz.getName + " is provided by the framework, and cannot be embedded in a user bundle. " +
- "To resolve this problem, please refer to osgi-classloading.html#multiple-implementations in the documentation"
-
- (for {
- resolvedClass <- Try {Class.forName(clazz.getName, false, this.getClass.getClassLoader)}
- if resolvedClass != clazz
- } yield errMsg)
- .getOrElse("")
- }
-
- // For unit testing
- def getNode(graph: ComponentGraph, componentId: String): Node = {
- graph.nodesById(new ComponentId(componentId))
- }
-
- private def nodesOfType[T <: Node](nodes: Traversable[Node], clazz : Class[T]) : Traversable[T] = {
- nodes.collect {
- case node if clazz.isInstance(node) => clazz.cast(node)
- }
- }
-
- private def matchingComponentNodes(nodes: Traversable[Node], key: Key[_]) : Traversable[ComponentNode] = {
- matchingNodes(nodes, classOf[ComponentNode], key)
- }
-
- // Finds all nodes with a given nodeType and instance with given key
- private def matchingNodes[T <: Node](nodes: Traversable[Node], nodeType: Class[T], key: Key[_]) : Traversable[T] = {
- val clazz = key.getTypeLiteral.getRawType
- val annotation = key.getAnnotation
-
- val filteredByClass = nodesOfType(nodes, nodeType) filter { node => clazz.isAssignableFrom(node.componentType) }
- val filteredByClassAndAnnotation = filteredByClass filter { node => annotation == node.instanceKey.getAnnotation }
-
- if (filteredByClass.size == 1) filteredByClass
- else if (filteredByClassAndAnnotation.size > 0) filteredByClassAndAnnotation
- else filteredByClass
- }
-
- // Returns true if annotation is a BindingAnnotation, e.g. com.google.inject.name.Named
- def isBindingAnnotation(annotation: JavaAnnotation) : Boolean = {
- def isBindingAnnotation(clazz: Class[_]) : Boolean = {
- val clazzOrSuperIsBindingAnnotation =
- (clazz.getAnnotation(classOf[BindingAnnotation]) != null) ||
- Option(clazz.getSuperclass).map(isBindingAnnotation).getOrElse(false)
-
- (clazzOrSuperIsBindingAnnotation /: clazz.getInterfaces.map(isBindingAnnotation))(_ || _)
- }
- isBindingAnnotation(annotation.getClass)
- }
-
- /**
- * The returned list is the nodes from the graph bottom-up.
- * @return A list where a earlier than b in the list implies that there is no path from a to b
- */
- def topologicalSort(nodes: List[Node]): List[Node] = {
- val numIncoming = mutable.Map[ComponentId, Int]().withDefaultValue(0)
-
- def forEachUsedComponent(nodes: Traversable[Node])(f: Node => Unit) {
- nodes.foreach(_.usedComponents.foreach(f))
- }
-
- def partitionByNoIncoming(nodes: List[Node]) =
- nodes.partition(node => numIncoming(node.componentId) == 0)
-
- @tailrec
- def sort(sorted: List[Node], unsorted: List[Node]) : List[Node] = {
- if (unsorted.isEmpty) {
- sorted
- } else {
- val (ready, notReady) = partitionByNoIncoming(unsorted)
- require(!ready.isEmpty, "There's a cycle in the graph.") //TODO: return cycle
- forEachUsedComponent(ready) { injectedNode => numIncoming(injectedNode.componentId) -= 1}
- sort(ready ::: sorted, notReady)
- }
- }
-
- forEachUsedComponent(nodes) { injectedNode => numIncoming(injectedNode.componentId) += 1 }
- sort(List(), nodes)
- }
-}
diff --git a/container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/ComponentNode.scala b/container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/ComponentNode.scala
deleted file mode 100644
index cc1745d6e35..00000000000
--- a/container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/ComponentNode.scala
+++ /dev/null
@@ -1,213 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.container.di.componentgraph.core
-
-import java.lang.reflect.{Constructor, InvocationTargetException, Modifier, ParameterizedType, Type}
-import java.util.logging.Logger
-
-import com.google.inject.Inject
-import com.yahoo.component.{AbstractComponent, ComponentId}
-import com.yahoo.config.ConfigInstance
-import com.yahoo.container.di.componentgraph.Provider
-import com.yahoo.container.di.componentgraph.core.ComponentNode._
-import com.yahoo.container.di.componentgraph.core.Node.equalEdges
-import com.yahoo.container.di.{ConfigKeyT, JavaAnnotation, createKey, makeClassCovariant, preserveStackTrace, removeStackTrace}
-import com.yahoo.vespa.config.ConfigKey
-
-import scala.language.postfixOps
-
-/**
- * @author Tony Vaagenes
- * @author gjoranv
- */
-class ComponentNode(componentId: ComponentId,
- val configId: String,
- clazz: Class[_ <: AnyRef],
- val XXX_key: JavaAnnotation = null) // TODO expose key, not javaAnnotation
- extends Node(componentId)
-{
- require(!isAbstract(clazz), "Can't instantiate abstract class " + clazz.getName)
-
- var arguments : Array[AnyRef] = _
-
- val constructor: Constructor[AnyRef] = bestConstructor(clazz)
-
- var availableConfigs: Map[ConfigKeyT, ConfigInstance] = null
-
- override val instanceKey = createKey(clazz, XXX_key)
-
- override val instanceType = clazz
-
- override def usedComponents: List[Node] = {
- require(arguments != null, "Arguments must be set first.")
- arguments.collect{case node: Node => node}.toList
- }
-
- override val componentType: Class[AnyRef] = {
- def allSuperClasses(clazz: Class[_], coll : List[Class[_]]) : List[Class[_]] = {
- if (clazz == null) coll
- else allSuperClasses(clazz.getSuperclass, clazz :: coll)
- }
-
- def allGenericInterfaces(clazz : Class[_]) = allSuperClasses(clazz, List()) flatMap (_.getGenericInterfaces)
-
- def isProvider = classOf[Provider[_]].isAssignableFrom(clazz)
- def providerComponentType = (allGenericInterfaces(clazz).collect {
- case t: ParameterizedType if t.getRawType == classOf[Provider[_]] => t.getActualTypeArguments.head
- }).head
-
- if (isProvider) providerComponentType.asInstanceOf[Class[AnyRef]] //TODO: Test what happens if you ask for something that isn't a class, e.g. a parametrized type.
- else clazz.asInstanceOf[Class[AnyRef]]
- }
-
- def setArguments(arguments: Array[AnyRef]) {
- this.arguments = arguments
- }
-
- def cutStackTraceAtConstructor(throwable: Throwable): Throwable = {
- def takeUntilComponentNode(elements: Array[StackTraceElement]) =
- elements.takeWhile(_.getClassName != classOf[ComponentNode].getName)
-
- def dropToInitAtEnd(elements: Array[StackTraceElement]) =
- elements.reverse.dropWhile(_.getMethodName != "<init>").reverse
-
- val modifyStackTrace = takeUntilComponentNode _ andThen dropToInitAtEnd
-
- val dependencyInjectorStackTraceMarker = new StackTraceElement("============= Dependency Injection =============", "newInstance", null, -1)
-
- if (throwable != null && !preserveStackTrace) {
- throwable.setStackTrace(modifyStackTrace(throwable.getStackTrace) :+
- dependencyInjectorStackTraceMarker)
-
- cutStackTraceAtConstructor(throwable.getCause)
- }
- throwable
- }
-
- override protected def newInstance() : AnyRef = {
- assert (arguments != null, "graph.complete must be called before retrieving instances.")
-
- val actualArguments = arguments.map {
- case node: Node => node.newOrCachedInstance()
- case config: ConfigKeyT => availableConfigs(config.asInstanceOf[ConfigKeyT])
- case other => other
- }
-
- val instance =
- try {
- constructor.newInstance(actualArguments: _*)
- } catch {
- case e: InvocationTargetException =>
- throw removeStackTrace(ErrorOrComponentConstructorException(cutStackTraceAtConstructor(e.getCause), s"Error constructing $idAndType"))
- }
-
- initId(instance)
- }
-
- private def ErrorOrComponentConstructorException(cause: Throwable, message: String) : Throwable = {
- if (cause != null && cause.isInstanceOf[Error]) // don't convert Errors to RuntimeExceptions
- new Error(message, cause)
- else
- new ComponentConstructorException(message, cause)
- }
-
- private def initId(component: AnyRef) = {
- def checkAndSetId(c: AbstractComponent) {
- if (c.hasInitializedId && c.getId != componentId )
- throw new IllegalStateException(
- s"Component with id '$componentId' is trying to set its component id explicitly: '${c.getId}'. " +
- "This is not allowed, so please remove any call to super() in your component's constructor.")
-
- c.initId(componentId)
- }
-
- component match {
- case component: AbstractComponent => checkAndSetId(component)
- case other => ()
- }
- component
- }
-
- override def equals(other: Any) = {
- other match {
- case that: ComponentNode =>
- super.equals(that) &&
- equalEdges(arguments.toList, that.arguments.toList) &&
- usedConfigs == that.usedConfigs
- }
- }
-
- private def usedConfigs = {
- require(availableConfigs != null, "setAvailableConfigs must be called!")
- ( arguments collect {case c: ConfigKeyT => c} map (availableConfigs) ).toList
- }
-
- def getAnnotatedConstructorParams: Array[(Type, Array[JavaAnnotation])] = {
- constructor.getGenericParameterTypes zip constructor.getParameterAnnotations
- }
-
- def setAvailableConfigs(configs: Map[ConfigKeyT, ConfigInstance]) {
- require (arguments != null, "graph.complete must be called before graph.setAvailableConfigs.")
- availableConfigs = configs
- }
-
- override def configKeys = {
- configParameterClasses.map(new ConfigKey(_, configId)).toSet
- }
-
-
- private def configParameterClasses: Array[Class[ConfigInstance]] = {
- constructor.getGenericParameterTypes.collect {
- case clazz: Class[_] if classOf[ConfigInstance].isAssignableFrom(clazz) => clazz.asInstanceOf[Class[ConfigInstance]]
- }
- }
-
- override def label = {
- val configNames = configKeys.map(_.getName + ".def").toList
-
- (List(instanceType.getSimpleName, Node.packageName(instanceType)) ::: configNames).
- mkString("{", "|", "}")
- }
-
-}
-
-object ComponentNode {
- val log = Logger.getLogger(classOf[ComponentNode].getName)
-
- private def bestConstructor(clazz: Class[AnyRef]) = {
- val publicConstructors = clazz.getConstructors.asInstanceOf[Array[Constructor[AnyRef]]]
-
- def constructorAnnotatedWithInject = {
- publicConstructors filter {_.getAnnotation(classOf[Inject]) != null} match {
- case Array() => None
- case Array(single) => Some(single)
- case _ => throwComponentConstructorException("Multiple constructors annotated with inject in class " + clazz.getName)
- }
- }
-
- def constructorWithMostConfigParameters = {
- def isConfigInstance(clazz: Class[_]) = classOf[ConfigInstance].isAssignableFrom(clazz)
-
- publicConstructors match {
- case Array() => throwComponentConstructorException("No public constructors in class " + clazz.getName)
- case Array(single) => single
- case _ =>
- log.warning("Multiple public constructors found in class %s, there should only be one. ".format(clazz.getName) +
- "If more than one public constructor is needed, the primary one must be annotated with @Inject.")
- publicConstructors.
- sortBy(_.getParameterTypes.filter(isConfigInstance).size).
- last
- }
- }
-
- constructorAnnotatedWithInject getOrElse constructorWithMostConfigParameters
- }
-
- private def throwComponentConstructorException(message: String) =
- throw removeStackTrace(new ComponentConstructorException(message))
-
- class ComponentConstructorException(message: String, cause: Throwable) extends RuntimeException(message, cause) {
- def this(message: String) = this(message, null)
- }
-
- def isAbstract(clazz: Class[_ <: AnyRef]) = Modifier.isAbstract(clazz.getModifiers)
-}
diff --git a/container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/ComponentRegistryNode.scala b/container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/ComponentRegistryNode.scala
deleted file mode 100644
index 9c7e2ba322f..00000000000
--- a/container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/ComponentRegistryNode.scala
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.container.di.componentgraph.core
-
-import com.yahoo.component.provider.ComponentRegistry
-import com.yahoo.component.{ComponentId, Component}
-import ComponentRegistryNode._
-import com.google.inject.Key
-import com.google.inject.util.Types
-import Node.syntheticComponentId
-
-/**
- * @author Tony Vaagenes
- * @author gjoranv
- */
-class ComponentRegistryNode(val componentClass : Class[AnyRef])
- extends Node(componentId(componentClass)) {
-
- def usedComponents = componentsToInject
-
- protected def newInstance() = {
- val registry = new ComponentRegistry[AnyRef]
-
- componentsToInject foreach { component =>
- registry.register(component.componentId, component.newOrCachedInstance())
- }
-
- registry
- }
-
- override val instanceKey =
- Key.get(Types.newParameterizedType(classOf[ComponentRegistry[_]], componentClass)).asInstanceOf[Key[AnyRef]]
-
- override val instanceType: Class[AnyRef] = instanceKey.getTypeLiteral.getRawType.asInstanceOf[Class[AnyRef]]
- override val componentType: Class[AnyRef] = instanceType
-
- override def configKeys = Set()
-
- override def equals(other: Any) = {
- other match {
- case that: ComponentRegistryNode =>
- componentId == that.componentId && // includes componentClass
- instanceType == that.instanceType &&
- equalEdges(usedComponents, that.usedComponents)
- case _ => false
- }
- }
-
- override def label =
- "{ComponentRegistry\\<%s\\>|%s}".format(componentClass.getSimpleName, Node.packageName(componentClass))
-}
-
-object ComponentRegistryNode {
- val componentRegistryNamespace = ComponentId.fromString("ComponentRegistry")
-
- def componentId(componentClass: Class[_]) = {
- syntheticComponentId(componentClass.getName, componentClass, componentRegistryNamespace)
- }
-
- def equalEdges(edges: List[Node], otherEdges: List[Node]): Boolean = {
- def compareEdges = {
- (sortByComponentId(edges) zip sortByComponentId(otherEdges)).
- forall(equalEdge)
- }
-
- def sortByComponentId(in: List[Node]) = in.sortBy(_.componentId)
- def equalEdge(edgePair: (Node, Node)): Boolean = edgePair._1.componentId == edgePair._2.componentId
-
- edges.size == otherEdges.size &&
- compareEdges
- }
-}
diff --git a/container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/GuiceNode.scala b/container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/GuiceNode.scala
deleted file mode 100644
index 26fb0e0d3d8..00000000000
--- a/container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/GuiceNode.scala
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.container.di.componentgraph.core
-
-import com.yahoo.container.di.{JavaAnnotation, createKey}
-import com.yahoo.component.ComponentId
-import Node.syntheticComponentId
-import GuiceNode._
-
-/**
- * @author Tony Vaagenes
- * @author gjoranv
- */
-final class GuiceNode(myInstance: AnyRef,
- annotation: JavaAnnotation) extends Node(componentId(myInstance)) {
-
- override def configKeys = Set()
-
- override val instanceKey = createKey(myInstance.getClass, annotation)
- override val instanceType = myInstance.getClass
- override val componentType = instanceType
-
-
- override def usedComponents = List()
-
- override protected def newInstance() = myInstance
-
- override def inject(component: Node) {
- throw new UnsupportedOperationException("Illegal to inject components to a GuiceNode!")
- }
-
- override def label =
- "{{%s|Guice}|%s}".format(instanceType.getSimpleName, Node.packageName(instanceType))
-}
-
-object GuiceNode {
- val guiceNamespace = ComponentId.fromString("Guice")
-
- def componentId(instance: AnyRef) = {
- syntheticComponentId(instance.getClass.getName, instance, guiceNamespace)
- }
-}
diff --git a/container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/JerseyNode.scala b/container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/JerseyNode.scala
deleted file mode 100644
index 68353c47124..00000000000
--- a/container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/JerseyNode.scala
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.container.di.componentgraph.core
-
-import java.net.URL
-
-import com.yahoo.component.{ComponentId, ComponentSpecification}
-import com.yahoo.container.di.Osgi
-import com.yahoo.container.di.Osgi.RelativePath
-import com.yahoo.container.di.componentgraph.core.JerseyNode._
-import com.yahoo.container.di.config.RestApiContext
-import com.yahoo.container.di.config.RestApiContext.BundleInfo
-import org.osgi.framework.Bundle
-import org.osgi.framework.wiring.BundleWiring
-
-import scala.collection.JavaConverters._
-
-/**
- * Represents an instance of RestApiContext
- *
- * @author gjoranv
- * @author Tony Vaagenes
- */
-class JerseyNode(componentId: ComponentId,
- override val configId: String,
- clazz: Class[_ <: RestApiContext],
- osgi: Osgi)
- extends ComponentNode(componentId, configId, clazz) {
-
-
- override protected def newInstance(): RestApiContext = {
- val instance = super.newInstance()
- val restApiContext = instance.asInstanceOf[RestApiContext]
-
- val bundles = restApiContext.bundlesConfig.bundles().asScala
- bundles foreach ( bundleConfig => {
- val bundleClasses = osgi.getBundleClasses(
- ComponentSpecification.fromString(bundleConfig.spec()),
- bundleConfig.packages().asScala.toSet)
-
- restApiContext.addBundle(
- createBundleInfo(bundleClasses.bundle, bundleClasses.classEntries))
- })
-
- componentsToInject foreach (
- component =>
- restApiContext.addInjectableComponent(component.instanceKey, component.componentId, component.newOrCachedInstance()))
-
- restApiContext
- }
-
- override def equals(other: Any): Boolean = {
- super.equals(other) && (other match {
- case that: JerseyNode => componentsToInject == that.componentsToInject
- case _ => false
- })
- }
-
-}
-
-private[core]
-object JerseyNode {
- val WebInfUrl = "WebInfUrl"
-
- def createBundleInfo(bundle: Bundle, classEntries: Iterable[RelativePath]): BundleInfo = {
-
- val bundleInfo = new BundleInfo(bundle.getSymbolicName,
- bundle.getVersion,
- bundle.getLocation,
- webInfUrl(bundle),
- bundle.adapt(classOf[BundleWiring]).getClassLoader)
-
- bundleInfo.setClassEntries(classEntries.asJavaCollection)
- bundleInfo
- }
-
-
- private def getBundle(osgi: Osgi, bundleSpec: String): Bundle = {
-
- val bundle = osgi.getBundle(ComponentSpecification.fromString(bundleSpec))
- if (bundle == null)
- throw new IllegalArgumentException("Bundle not found: " + bundleSpec)
- bundle
- }
-
- private def webInfUrl(bundle: Bundle): URL = {
- val strWebInfUrl = bundle.getHeaders.get(WebInfUrl)
-
- if (strWebInfUrl == null) null
- else bundle.getEntry(strWebInfUrl)
- }
-
-}
diff --git a/container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/Node.scala b/container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/Node.scala
deleted file mode 100644
index d2476904e39..00000000000
--- a/container-di/src/main/scala/com/yahoo/container/di/componentgraph/core/Node.scala
+++ /dev/null
@@ -1,117 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.container.di.componentgraph.core
-
-import java.util.logging.Logger
-
-import com.google.inject.Key
-import com.yahoo.component.ComponentId
-import com.yahoo.container.di.ConfigKeyT
-import com.yahoo.container.di.componentgraph.Provider
-import com.yahoo.container.di.componentgraph.core.Node._
-import com.yahoo.log.LogLevel.{DEBUG, SPAM}
-
-/**
- * @author Tony Vaagenes
- * @author gjoranv
- */
-abstract class Node(val componentId: ComponentId) {
-
- def instanceKey: Key[AnyRef]
-
- var instance : Option[AnyRef] = None
-
- var componentsToInject = List[Node]()
-
- /**
- * The components actually used by this node.
- * Consist of a subset of the injected nodes + subset of the global nodes.
- */
- def usedComponents: List[Node]
-
- protected def newInstance() : AnyRef
-
- def newOrCachedInstance() : AnyRef = {
- val inst = if (instance.isEmpty) {
- log.log(DEBUG, s"Creating new instance for component with ID $componentId")
- instance = Some(newInstance())
- instance.get
- } else {
- log.log(SPAM, s"Reusing instance for component with ID $componentId")
- instance.get
- }
- component(inst)
- }
-
- private def component(instance: AnyRef) = instance match {
- case provider: Provider[_] => provider.get().asInstanceOf[AnyRef]
- case other => other
- }
-
- def configKeys: Set[ConfigKeyT]
-
- def inject(component: Node) {
- componentsToInject ::= component
- }
-
- def injectAll(componentNodes: Traversable[ComponentNode]) {
- componentNodes.foreach(inject(_))
- }
-
- def instanceType: Class[_ <: AnyRef]
- def componentType: Class[_ <: AnyRef]
-
- override def equals(other: Any) = {
- other match {
- case that: Node =>
- getClass == that.getClass &&
- componentId == that.componentId &&
- instanceType == that.instanceType &&
- equalEdges(usedComponents, that.usedComponents)
- case _ => false
- }
- }
-
- def label: String
-
- def idAndType = {
- val className = instanceType.getName
-
- if (className == componentId.getName) s"'$componentId'"
- else s"'$componentId' of type '$className'"
- }
-
-}
-
-object Node {
- private val log = Logger.getLogger(classOf[Node].getName)
-
- def equalEdges(edges1: List[AnyRef], edges2: List[AnyRef]): Boolean = {
- def compare(objects: (AnyRef, AnyRef)): Boolean = {
- objects match {
- case (edge1: Node, edge2: Node) => equalEdge(edge1, edge2)
- case (o1, o2) => o1 == o2
- }
- }
-
- def equalEdge(e1: Node, e2: Node) = e1.componentId == e2.componentId
-
- (edges1 zip edges2).forall(compare)
- }
-
- /**
- * @param identityObject The identifying object that makes the Node unique
- */
- private[componentgraph]
- def syntheticComponentId(className: String, identityObject: AnyRef, namespace: ComponentId) = {
- val name = className + "_" + System.identityHashCode(identityObject)
- ComponentId.fromString(name).nestInNamespace(namespace)
- }
-
-
- def packageName(componentClass: Class[_]) = {
- def nullIfNotFound(index : Int) = if (index == -1) 0 else index
-
- val fullClassName = componentClass.getName
- fullClassName.substring(0, nullIfNotFound(fullClassName.lastIndexOf(".")))
- }
-}
diff --git a/container-di/src/main/scala/com/yahoo/container/di/osgi/OsgiUtil.scala b/container-di/src/main/scala/com/yahoo/container/di/osgi/OsgiUtil.scala
deleted file mode 100644
index 3769eed6d2d..00000000000
--- a/container-di/src/main/scala/com/yahoo/container/di/osgi/OsgiUtil.scala
+++ /dev/null
@@ -1,143 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.container.di.osgi
-
-import java.nio.file.{Files, Path, Paths}
-import java.util.function.Predicate
-import java.util.jar.{JarEntry, JarFile}
-import java.util.logging.{Level, Logger}
-import java.util.stream.Collectors
-
-import com.google.common.io.Files.fileTreeTraverser
-import com.yahoo.component.ComponentSpecification
-import com.yahoo.container.di.Osgi.RelativePath
-import com.yahoo.osgi.maven.ProjectBundleClassPaths
-import com.yahoo.osgi.maven.ProjectBundleClassPaths.BundleClasspathMapping
-import org.osgi.framework.Bundle
-import org.osgi.framework.wiring.BundleWiring
-
-import scala.collection.JavaConverters._
-
-/**
- * Tested by com.yahoo.application.container.jersey.JerseyTest
- * @author Tony Vaagenes
- */
-object OsgiUtil {
- private val log = Logger.getLogger(getClass.getName)
- private val classFileTypeSuffix = ".class"
-
- def getClassEntriesInBundleClassPath(bundle: Bundle, packagesToScan: Set[String]) = {
- val bundleWiring = bundle.adapt(classOf[BundleWiring])
-
- def listClasses(path: String, recurse: Boolean): Iterable[RelativePath] = {
- val options =
- if (recurse) BundleWiring.LISTRESOURCES_LOCAL | BundleWiring.LISTRESOURCES_RECURSE
- else BundleWiring.LISTRESOURCES_LOCAL
-
- bundleWiring.listResources(path, "*" + classFileTypeSuffix, options).asScala
- }
-
- if (packagesToScan.isEmpty) listClasses("/", recurse = true)
- else packagesToScan flatMap { packageName => listClasses(packageToPath(packageName), recurse = false) }
- }
-
- def getClassEntriesForBundleUsingProjectClassPathMappings(classLoader: ClassLoader,
- bundleSpec: ComponentSpecification,
- packagesToScan: Set[String]) = {
- classEntriesFrom(
- bundleClassPathMapping(bundleSpec, classLoader).classPathElements.asScala.toList,
- packagesToScan)
- }
-
- private def bundleClassPathMapping(bundleSpec: ComponentSpecification,
- classLoader: ClassLoader): BundleClasspathMapping = {
-
- val projectBundleClassPaths = loadProjectBundleClassPaths(classLoader)
-
- if (projectBundleClassPaths.mainBundle.bundleSymbolicName == bundleSpec.getName) {
- projectBundleClassPaths.mainBundle
- } else {
- log.log(Level.WARNING, s"Dependencies of the bundle $bundleSpec will not be scanned. Please file a feature request if you need this" )
- matchingBundleClassPathMapping(bundleSpec, projectBundleClassPaths.providedDependencies.asScala.toList)
- }
- }
-
- def matchingBundleClassPathMapping(bundleSpec: ComponentSpecification,
- providedBundlesClassPathMappings: List[BundleClasspathMapping]): BundleClasspathMapping = {
- providedBundlesClassPathMappings.
- find(_.bundleSymbolicName == bundleSpec.getName).
- getOrElse(throw new RuntimeException("No such bundle: " + bundleSpec))
- }
-
- private def loadProjectBundleClassPaths(classLoader: ClassLoader): ProjectBundleClassPaths = {
- val classPathMappingsFileLocation = classLoader.getResource(ProjectBundleClassPaths.CLASSPATH_MAPPINGS_FILENAME)
- if (classPathMappingsFileLocation == null)
- throw new RuntimeException(s"Couldn't find ${ProjectBundleClassPaths.CLASSPATH_MAPPINGS_FILENAME} in the class path.")
-
- ProjectBundleClassPaths.load(Paths.get(classPathMappingsFileLocation.toURI))
- }
-
- private def classEntriesFrom(classPathEntries: List[String], packagesToScan: Set[String]): Iterable[RelativePath] = {
- val packagePathsToScan = packagesToScan map packageToPath
-
- classPathEntries.flatMap { entry =>
- val path = Paths.get(entry)
- if (Files.isDirectory(path)) classEntriesInPath(path, packagePathsToScan)
- else if (Files.isRegularFile(path) && path.getFileName.toString.endsWith(".jar")) classEntriesInJar(path, packagePathsToScan)
- else throw new RuntimeException("Unsupported path " + path + " in the class path")
- }
- }
-
- private def classEntriesInPath(rootPath: Path, packagePathsToScan: Traversable[String]): Traversable[RelativePath] = {
- def relativePathToClass(pathToClass: Path): RelativePath = {
- val relativePath = rootPath.relativize(pathToClass)
- relativePath.toString
- }
-
- val fileIterator =
- if (packagePathsToScan.isEmpty) fileTreeTraverser().preOrderTraversal(rootPath.toFile).asScala
- else packagePathsToScan.view flatMap { packagePath => fileTreeTraverser().children(rootPath.resolve(packagePath).toFile).asScala }
-
- for {
- file <- fileIterator
- if file.isFile
- if file.getName.endsWith(classFileTypeSuffix)
- } yield relativePathToClass(file.toPath)
- }
-
-
- private def classEntriesInJar(jarPath: Path, packagePathsToScan: Set[String]): Traversable[RelativePath] = {
- def packagePath(name: String) = {
- name.lastIndexOf('/') match {
- case -1 => name
- case n => name.substring(0, n)
- }
- }
-
- val acceptedPackage: Predicate[String] =
- if (packagePathsToScan.isEmpty) (name: String) => true
- else (name: String) => packagePathsToScan(packagePath(name))
-
- var jarFile: JarFile = null
- try {
- jarFile = new JarFile(jarPath.toFile)
- jarFile.stream().
- map[String] { entry: JarEntry => entry.getName}.
- filter { name: String => name.endsWith(classFileTypeSuffix)}.
- filter(acceptedPackage).
- collect(Collectors.toList()).
- asScala
- } finally {
- if (jarFile != null) jarFile.close()
- }
- }
-
- def packageToPath(packageName: String) = packageName.replaceAllLiterally(".", "/")
-
- implicit class JavaPredicate[T](f: T => Boolean) extends Predicate[T] {
- override def test(t: T): Boolean = f(t)
- }
-
- implicit class JavaFunction[T, R](f: T => R) extends java.util.function.Function[T, R] {
- override def apply(t: T): R = f(t)
- }
-}
diff --git a/container-di/src/main/scala/com/yahoo/container/di/package.scala b/container-di/src/main/scala/com/yahoo/container/di/package.scala
deleted file mode 100644
index cccb0242e8b..00000000000
--- a/container-di/src/main/scala/com/yahoo/container/di/package.scala
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.container
-
-import java.lang.reflect.Type
-
-import com.google.inject.Key
-import com.yahoo.config.ConfigInstance
-import com.yahoo.vespa.config.ConfigKey
-
-import scala.language.implicitConversions
-
-/**
- *
- * @author gjoranv
- * @author Tony Vaagenes
- */
-package object di {
- type ConfigKeyT = ConfigKey[_ <: ConfigInstance]
- type GuiceInjector = com.google.inject.Injector
- type JavaAnnotation = java.lang.annotation.Annotation
-
- def createKey(instanceType: Type, annotation: JavaAnnotation) = {
- {if (annotation == null)
- Key.get(instanceType)
- else
- Key.get(instanceType, annotation)
- }.asInstanceOf[Key[AnyRef]]
- }
-
- implicit def makeClassCovariant[SUB, SUPER >: SUB](clazz: Class[SUB]) : Class[SUPER] = {
- clazz.asInstanceOf[Class[SUPER]]
- }
-
- def removeStackTrace(exception: Throwable): Throwable = {
- if (preserveStackTrace) exception
- else {
- exception.setStackTrace(Array())
- exception
- }
- }
-
- //For debug purposes only
- val preserveStackTrace: Boolean = Option(System.getProperty("jdisc.container.preserveStackTrace")).filterNot(_.isEmpty).isDefined
-}