Summary
Answers the open questions left by GDN-NOTE-0039 (wave B). Ownership layering: container-disc owns the DI runtime (packages com.yahoo.container.di and com.yahoo.osgi); config-model produces the wiring inputs (ComponentsConfig, PlatformBundlesConfig, ApplicationBundlesConfig); jdisc_core owns the OSGi framework and process bootstrap. Config payloads arrive through the config-admin pipeline traced in GDN-NOTE-0036. Process chain: StandaloneMain.main (StandaloneMain.java:28) builds new ApplicationLoader(Main.newOsgiFramework(), Main.newConfigModule()) (StandaloneMain.java:33-35; Main.java:16-28 creates FelixFramework), then loader.init/start (ApplicationLoader.java:96,129) boots ConfiguredApplication (ConfiguredApplication.java:172), which drives HandlersConfigurerDi -> com.yahoo.container.di.Container. All of this runs inside container-jdisc; sibling notes in index.
Graph construction order
Owner: di.Container, driven by HandlersConfigurerDi.waitForNextGraphGeneration (HandlersConfigurerDi.java:156; di/Container.java:81).
- ConfigRetriever holds a fixed “bootstrap” subscriber for ApplicationBundlesConfig, PlatformBundlesConfig and ComponentsConfig (di/Container.java:74-78; ConfigRetriever.java:48) plus a dynamic “component” subscriber that is re-created whenever its key set changes (ConfigRetriever.java:81,154).
- BootstrapConfigs snapshot (new bootstrap generation): platform bundles install only at graph generation 0 (di/Container.java:168-169); any later platform-bundle change throws (lines 171,208); application bundles install (line 173); a preliminary graph is built and the loop continues.
- ComponentsConfigs snapshot: createAndConfigureComponentGraph = createComponentGraph + setAvailableConfigs (di/Container.java:213-217).
- Graph build (di/Container.java:219-246): one ComponentNode per ComponentsConfig.Components entry, class resolved via osgi.resolveClass (OSGi bundle lookup); injectNodes wires explicit inject edges from config; then graph.complete(fallbackInjector).
- complete() (ComponentGraph.java:130-134) resolves each annotated constructor parameter to a placeholder: ConfigInstance parameters become ConfigKey(class, configId) handles (lines 199,242-247); ComponentRegistry parameters create or reuse a ComponentRegistryNode (lines 202,212-238, with “TODO: don’t mutate nodes here” at line 214); plain-class parameters resolve to a global component node or fall back to a Guice fallback injector binding Container, AccessLog, Executor and FileAcquirer (HandlersConfigurerDi.java:177-186), logging “should probably be declared in services.xml” (ComponentGraph.java:256-266).
- reuseNodes(oldGraph) copies instances of unchanged nodes and resets nodes whose dependencies changed (di/Container.java:85; ComponentGraph.java:148-166).
- constructComponents calls Node.constructInstance on all nodes (di/Container.java:132-137). Construction is lazy and demand-driven, not topological: Node.component() constructs on first access (Node.java:42-56), and ComponentNode.newInstance() pulls Node arguments through component() and ConfigInstance arguments through getConfigInstance (ComponentNode.java:136-158,241-249). topologicalSort (ComponentGraph.java:396-425) orders deconstruction (reversed, lines 168-173) and reuseNodes iteration, not construction.
- Constructor selection: the @Inject-annotated public constructor (google.inject or com.yahoo.component.annotation), else the only public one, else a most-config-parameters heuristic (ComponentNode.java:276-310).
Config injection binding: config is never bound in Guice. The node’s configId plus the constructor-parameter ConfigInstance type form a ConfigKey looked up in the graph-wide availableConfigs map at construction time; those keys feed the dynamic subscriber via ComponentGraph.configKeys() (ComponentGraph.java:139-141).
Failure handling: graph-creation failure reverts the new bundles and forces bootstrap-first recovery with empty keys (di/Container.java:88-100); construction failure keeps the failed graph’s keys for recovery (lines 105-113); invalidation sets leastGeneration to max(components, bootstrap) + 1 (line 247-251).
OSGi ownership
The production di.Osgi implementation is HandlersConfigurerDi.ContainerAndDiOsgi (inner class, HandlersConfigurerDi.java:84-137): it extends com.yahoo.osgi.OsgiImpl and implements OsgiWrapper, which bridges com.yahoo.osgi.Osgi and com.yahoo.container.di.Osgi (OsgiWrapper.java:10). It is created per container in ConfiguredApplication.createConfigurer (ConfiguredApplication.java:451-458) with the real jdisc OsgiFramework injected into ConfiguredApplication (line 152-171); application code instead gets a restricted DisableOsgiFramework binding (line 466). Bundle lists come from config-model: ContainerCluster implements PlatformBundlesConfig.Producer (ContainerCluster.java:97-103, 559-561) and ApplicationContainerCluster implements ApplicationBundlesConfig.Producer (ApplicationContainerCluster.java:68,300). The di.Osgi interface itself ships no-op defaults with test scaffolding (MockBundle, Class.forName resolveClass; di/Osgi.java:17,53-63) for non-OSGi tests. Install paths: PlatformBundleLoader installs disk bundles once, only under a Felix framework, and never uninstalls (HandlersConfigurerDi.java:99-105; PlatformBundleLoader.java:16,43-58); ApplicationBundleLoader installs FileReference bundles via file distribution, starts them, then commits or reverts per generation (ApplicationBundleLoader.java:64-135), with uninstalls left to the Deconstructor (di/Container.java:220-241). OsgiImpl.resolveClass loads from the named bundle, else the container-disc classpath, else the jdisc_core system bundle (OsgiImpl.java:57-101,183-197).
ApplicationStatus whereabouts
The handler is com.yahoo.container.handler.observability.ApplicationStatusHandler in container-disc (ApplicationStatusHandler.java:52, javadoc line 46), exposing deployed-application metadata plus an Extension hook (line 54) implemented by SearchStatusExtension (container-search) and DocprocsStatusExtension (docproc). Wiring is config-model side: ContainerCluster.APPLICATION_STATUS_HANDLER_CLASS (ContainerCluster.java:128) and addApplicationStatusHandler, which binds http path /ApplicationStatus with null bundle “as the handler is in container-disc” (lines 260-266). container-search-and-docproc has no src/ (only CMakeLists.txt, OWNERS, README.md, pom.xml), so README.md:7 (“This modules also contains the ApplicationStatus handler”) is stale; filed as GDN-PLAN-0004 candidate 1.
Open questions
- Deployed-container entrypoint: StandaloneMain exists, but whether production images launch com.yahoo.jdisc.core.StandaloneMain or com.yahoo.jdisc.utils.MinimalMain (MinimalMain.java:71) was not verified (no Dockerfile or launch script read).
- ComponentRegistryNode and GuiceNode internals (usedComponents semantics for providers) were not read; deconstruction-order claims rest on ComponentGraph.allConstructedComponentsAndProviders only.
- BundleStarter and DiskBundleInstaller (callers of Felix start/install) were seen but not read.