org.opensearch.common.inject.spi.Elements 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.spi;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.opensearch.common.inject.AbstractModule;
import org.opensearch.common.inject.Binder;
import org.opensearch.common.inject.Key;
import org.opensearch.common.inject.MembersInjector;
import org.opensearch.common.inject.Module;
import org.opensearch.common.inject.PrivateBinder;
import org.opensearch.common.inject.PrivateModule;
import org.opensearch.common.inject.Provider;
import org.opensearch.common.inject.Scope;
import org.opensearch.common.inject.Stage;
import org.opensearch.common.inject.TypeLiteral;
import org.opensearch.common.inject.binder.AnnotatedBindingBuilder;
import org.opensearch.common.inject.binder.AnnotatedConstantBindingBuilder;
import org.opensearch.common.inject.binder.AnnotatedElementBuilder;
import org.opensearch.common.inject.internal.AbstractBindingBuilder;
import org.opensearch.common.inject.internal.BindingBuilder;
import org.opensearch.common.inject.internal.ConstantBindingBuilderImpl;
import org.opensearch.common.inject.internal.Errors;
import org.opensearch.common.inject.internal.ExposureBuilder;
import org.opensearch.common.inject.internal.PrivateElementsImpl;
import org.opensearch.common.inject.internal.ProviderMethodsModule;
import org.opensearch.common.inject.internal.SourceProvider;
import org.opensearch.common.inject.matcher.Matcher;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Exposes elements of a module so they can be inspected, validated or {@link
* Element#applyTo(Binder) rewritten}.
*
* @author [email protected] (Jesse Wilson)
* @since 2.0
*
* @opensearch.internal
*/
public final class Elements {
/**
* Records the elements executed by {@code modules}.
*/
public static List getElements(Module... modules) {
return getElements(Stage.DEVELOPMENT, Arrays.asList(modules));
}
/**
* Records the elements executed by {@code modules}.
*/
public static List getElements(Iterable extends Module> modules) {
return getElements(Stage.DEVELOPMENT, modules);
}
/**
* Records the elements executed by {@code modules}.
*/
public static List getElements(Stage stage, Iterable extends Module> modules) {
RecordingBinder binder = new RecordingBinder(stage);
for (Module module : modules) {
binder.install(module);
}
return Collections.unmodifiableList(binder.elements);
}
/**
* Returns the module composed of {@code elements}.
*/
public static Module getModule(final Iterable extends Element> elements) {
return new Module() {
@Override
public void configure(Binder binder) {
for (Element element : elements) {
element.applyTo(binder);
}
}
};
}
/**
* A recording binder
*
* @opensearch.internal
*/
private static class RecordingBinder implements Binder, PrivateBinder {
private final Stage stage;
private final Set modules;
private final List elements;
private final Object source;
private final SourceProvider sourceProvider;
/**
* The binder where exposed bindings will be created
*/
private final RecordingBinder parent;
private final PrivateElementsImpl privateElements;
private RecordingBinder(Stage stage) {
this.stage = stage;
this.modules = new HashSet<>();
this.elements = new ArrayList<>();
this.source = null;
this.sourceProvider = new SourceProvider().plusSkippedClasses(
Elements.class,
RecordingBinder.class,
AbstractModule.class,
ConstantBindingBuilderImpl.class,
AbstractBindingBuilder.class,
BindingBuilder.class
);
this.parent = null;
this.privateElements = null;
}
/**
* Creates a recording binder that's backed by {@code prototype}.
*/
private RecordingBinder(RecordingBinder prototype, Object source, SourceProvider sourceProvider) {
if (!(source == null ^ sourceProvider == null)) {
throw new IllegalArgumentException();
}
this.stage = prototype.stage;
this.modules = prototype.modules;
this.elements = prototype.elements;
this.source = source;
this.sourceProvider = sourceProvider;
this.parent = prototype.parent;
this.privateElements = prototype.privateElements;
}
/**
* Creates a private recording binder.
*/
private RecordingBinder(RecordingBinder parent, PrivateElementsImpl privateElements) {
this.stage = parent.stage;
this.modules = new HashSet<>();
this.elements = privateElements.getElementsMutable();
this.source = parent.source;
this.sourceProvider = parent.sourceProvider;
this.parent = parent;
this.privateElements = privateElements;
}
@Override
public void bindScope(Class extends Annotation> annotationType, Scope scope) {
elements.add(new ScopeBinding(getSource(), annotationType, scope));
}
@Override
@SuppressWarnings("unchecked") // it is safe to use the type literal for the raw type
public void requestInjection(Object instance) {
requestInjection((TypeLiteral) TypeLiteral.get(instance.getClass()), instance);
}
@Override
public void requestInjection(TypeLiteral type, T instance) {
elements.add(new InjectionRequest<>(getSource(), type, instance));
}
@Override
public MembersInjector getMembersInjector(final TypeLiteral typeLiteral) {
final MembersInjectorLookup element = new MembersInjectorLookup<>(getSource(), typeLiteral);
elements.add(element);
return element.getMembersInjector();
}
@Override
public MembersInjector getMembersInjector(Class type) {
return getMembersInjector(TypeLiteral.get(type));
}
@Override
public void bindListener(Matcher super TypeLiteral>> typeMatcher, TypeListener listener) {
elements.add(new TypeListenerBinding(getSource(), listener, typeMatcher));
}
@Override
public void requestStaticInjection(Class>... types) {
for (Class> type : types) {
elements.add(new StaticInjectionRequest(getSource(), type));
}
}
@Override
public void install(Module module) {
if (modules.add(module)) {
Binder binder = this;
if (module instanceof PrivateModule) {
binder = binder.newPrivateBinder();
}
try {
module.configure(binder);
} catch (IllegalArgumentException e) {
// NOTE: This is not in the original guice. We rethrow here to expose any explicit errors in configure()
throw e;
} catch (RuntimeException e) {
Collection messages = Errors.getMessagesFromThrowable(e);
if (!messages.isEmpty()) {
elements.addAll(messages);
} else {
addError(e);
}
}
binder.install(ProviderMethodsModule.forModule(module));
}
}
@Override
public Stage currentStage() {
return stage;
}
@Override
public void addError(String message, Object... arguments) {
elements.add(new Message(getSource(), Errors.format(message, arguments)));
}
@Override
public void addError(Throwable t) {
String message = "An exception was caught and reported. Message: " + t.getMessage();
elements.add(new Message(Collections.singletonList(getSource()), message, t));
}
@Override
public void addError(Message message) {
elements.add(message);
}
@Override
public AnnotatedBindingBuilder bind(Key key) {
return new BindingBuilder<>(this, elements, getSource(), key);
}
@Override
public AnnotatedBindingBuilder bind(TypeLiteral typeLiteral) {
return bind(Key.get(typeLiteral));
}
@Override
public AnnotatedBindingBuilder bind(Class type) {
return bind(Key.get(type));
}
@Override
public AnnotatedConstantBindingBuilder bindConstant() {
return new ConstantBindingBuilderImpl(this, elements, getSource());
}
@Override
public Provider getProvider(final Key key) {
final ProviderLookup element = new ProviderLookup<>(getSource(), key);
elements.add(element);
return element.getProvider();
}
@Override
public Provider getProvider(Class type) {
return getProvider(Key.get(type));
}
@Override
public void convertToTypes(Matcher super TypeLiteral>> typeMatcher, TypeConverter converter) {
elements.add(new TypeConverterBinding(getSource(), typeMatcher, converter));
}
@Override
public RecordingBinder withSource(final Object source) {
return new RecordingBinder(this, source, null);
}
@Override
public RecordingBinder skipSources(Class... classesToSkip) {
// if a source is specified explicitly, we don't need to skip sources
if (source != null) {
return this;
}
SourceProvider newSourceProvider = sourceProvider.plusSkippedClasses(classesToSkip);
return new RecordingBinder(this, null, newSourceProvider);
}
@Override
public PrivateBinder newPrivateBinder() {
PrivateElementsImpl privateElements = new PrivateElementsImpl(getSource());
elements.add(privateElements);
return new RecordingBinder(this, privateElements);
}
@Override
public void expose(Key> key) {
exposeInternal(key);
}
@Override
public AnnotatedElementBuilder expose(Class> type) {
return exposeInternal(Key.get(type));
}
@Override
public AnnotatedElementBuilder expose(TypeLiteral> type) {
return exposeInternal(Key.get(type));
}
private AnnotatedElementBuilder exposeInternal(Key key) {
if (privateElements == null) {
addError("Cannot expose %s on a standard binder. " + "Exposed bindings are only applicable to private binders.", key);
return new AnnotatedElementBuilder() {
@Override
public void annotatedWith(Class extends Annotation> annotationType) {}
@Override
public void annotatedWith(Annotation annotation) {}
};
}
ExposureBuilder builder = new ExposureBuilder<>(this, getSource(), key);
privateElements.addExposureBuilder(builder);
return builder;
}
private static Logger logger = LogManager.getLogger(Elements.class);
protected Object getSource() {
Object ret;
if (logger.isDebugEnabled()) {
ret = sourceProvider != null ? sourceProvider.get() : source;
} else {
ret = source;
}
return ret == null ? "_unknown_" : ret;
}
@Override
public String toString() {
return "Binder";
}
}
}