org.opensearch.common.inject.InjectorShell Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of opensearch Show documentation
Show all versions of opensearch Show documentation
OpenSearch subproject :server
/*
* 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 [email protected] (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 extends Module> 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);
}
}
}