/* * SPDX-License-Identifier: Apache-2.0 * * The OpenSearch Contributors require contributions made to * this file be licensed under the Apache-2.0 license or a * compatible open source license. */ /* * Copyright (C) 2008 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Modifications Copyright OpenSearch Contributors. See * GitHub history for details. */ package org.opensearch.common.inject; import org.opensearch.common.inject.internal.Errors; import org.opensearch.common.inject.internal.ErrorsException; import org.opensearch.common.inject.internal.InternalContext; import org.opensearch.common.inject.internal.InternalFactory; import org.opensearch.common.inject.internal.PrivateElementsImpl; import org.opensearch.common.inject.internal.ProviderInstanceBindingImpl; import org.opensearch.common.inject.internal.Scoping; import org.opensearch.common.inject.internal.SourceProvider; import org.opensearch.common.inject.internal.Stopwatch; import org.opensearch.common.inject.spi.Dependency; import org.opensearch.common.inject.spi.Element; import org.opensearch.common.inject.spi.Elements; import org.opensearch.common.inject.spi.InjectionPoint; import org.opensearch.common.inject.spi.PrivateElements; import org.opensearch.common.inject.spi.TypeListenerBinding; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.logging.Logger; import static java.util.Collections.emptySet; import static org.opensearch.common.inject.Scopes.SINGLETON; /** * A partially-initialized injector. See {@link InjectorBuilder}, which uses this to build a tree * of injectors in batch. * * @author jessewilson@google.com (Jesse Wilson) * * @opensearch.internal */ class InjectorShell { private final List elements; private final InjectorImpl injector; private InjectorShell(List elements, InjectorImpl injector) { this.elements = elements; this.injector = injector; } InjectorImpl getInjector() { return injector; } List getElements() { return elements; } /** * Builder for an injector * * @opensearch.internal */ static class Builder { private final List elements = new ArrayList<>(); private final List modules = new ArrayList<>(); /** * lazily constructed */ private State state; private InjectorImpl parent; private Stage stage; /** * null unless this exists in a {@link Binder#newPrivateBinder private environment} */ private PrivateElementsImpl privateElements; Builder parent(InjectorImpl parent) { this.parent = parent; this.state = new InheritingState(parent.state); return this; } Builder stage(Stage stage) { this.stage = stage; return this; } Builder privateElements(PrivateElements privateElements) { this.privateElements = (PrivateElementsImpl) privateElements; this.elements.addAll(privateElements.getElements()); return this; } void addModules(Iterable modules) { for (Module module : modules) { this.modules.add(module); } } /** * Synchronize on this before calling {@link #build}. */ Object lock() { return getState().lock(); } /** * Creates and returns the injector shells for the current modules. Multiple shells will be * returned if any modules contain {@link Binder#newPrivateBinder private environments}. The * primary injector will be first in the returned list. */ List build(Initializer initializer, BindingProcessor bindingProcessor, Stopwatch stopwatch, Errors errors) { if (stage == null) { throw new IllegalStateException("Stage not initialized"); } if (privateElements != null && parent == null) { throw new IllegalStateException("PrivateElements with no parent"); } if (state == null) { throw new IllegalStateException("no state. Did you remember to lock() ?"); } InjectorImpl injector = new InjectorImpl(state, initializer); if (privateElements != null) { privateElements.initInjector(injector); } // bind Stage and Singleton if this is a top-level injector if (parent == null) { modules.add(0, new RootModule(stage)); new TypeConverterBindingProcessor(errors).prepareBuiltInConverters(injector); } elements.addAll(Elements.getElements(stage, modules)); stopwatch.resetAndLog("Module execution"); new MessageProcessor(errors).process(injector, elements); new TypeListenerBindingProcessor(errors).process(injector, elements); List listenerBindings = injector.state.getTypeListenerBindings(); injector.membersInjectorStore = new MembersInjectorStore(injector, listenerBindings); stopwatch.resetAndLog("TypeListeners creation"); new ScopeBindingProcessor(errors).process(injector, elements); stopwatch.resetAndLog("Scopes creation"); new TypeConverterBindingProcessor(errors).process(injector, elements); stopwatch.resetAndLog("Converters creation"); bindInjector(injector); bindLogger(injector); bindingProcessor.process(injector, elements); stopwatch.resetAndLog("Binding creation"); List injectorShells = new ArrayList<>(); injectorShells.add(new InjectorShell(elements, injector)); // recursively build child shells PrivateElementProcessor processor = new PrivateElementProcessor(errors, stage); processor.process(injector, elements); for (Builder builder : processor.getInjectorShellBuilders()) { injectorShells.addAll(builder.build(initializer, bindingProcessor, stopwatch, errors)); } stopwatch.resetAndLog("Private environment creation"); return injectorShells; } private State getState() { if (state == null) { state = new InheritingState(State.NONE); } return state; } } /** * The Injector is a special case because we allow both parent and child injectors to both have * a binding for that key. */ private static void bindInjector(InjectorImpl injector) { Key key = Key.get(Injector.class); InjectorFactory injectorFactory = new InjectorFactory(injector); injector.state.putBinding( key, new ProviderInstanceBindingImpl<>( injector, key, SourceProvider.UNKNOWN_SOURCE, injectorFactory, Scoping.UNSCOPED, injectorFactory, emptySet() ) ); } /** * The factory for the injector * * @opensearch.internal */ private static class InjectorFactory implements InternalFactory, Provider { private final Injector injector; private InjectorFactory(Injector injector) { this.injector = injector; } @Override public Injector get(Errors errors, InternalContext context, Dependency dependency) throws ErrorsException { return injector; } @Override public Injector get() { return injector; } @Override public String toString() { return "Provider"; } } /** * The Logger is a special case because it knows the injection point of the injected member. It's * the only binding that does this. */ private static void bindLogger(InjectorImpl injector) { Key key = Key.get(Logger.class); LoggerFactory loggerFactory = new LoggerFactory(); injector.state.putBinding( key, new ProviderInstanceBindingImpl<>( injector, key, SourceProvider.UNKNOWN_SOURCE, loggerFactory, Scoping.UNSCOPED, loggerFactory, emptySet() ) ); } /** * Factory for a logger * * @opensearch.internal */ private static class LoggerFactory implements InternalFactory, Provider { @Override public Logger get(Errors errors, InternalContext context, Dependency dependency) { InjectionPoint injectionPoint = dependency.getInjectionPoint(); return injectionPoint == null ? Logger.getAnonymousLogger() : Logger.getLogger(injectionPoint.getMember().getDeclaringClass().getName()); } @Override public Logger get() { return Logger.getAnonymousLogger(); } @Override public String toString() { return "Provider"; } } /** * The root module * * @opensearch.internal */ private static class RootModule implements Module { final Stage stage; private RootModule(Stage stage) { this.stage = Objects.requireNonNull(stage, "stage"); } @Override public void configure(Binder binder) { binder = binder.withSource(SourceProvider.UNKNOWN_SOURCE); binder.bind(Stage.class).toInstance(stage); binder.bindScope(Singleton.class, SINGLETON); } } }