/* * 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.Annotations; import org.opensearch.common.inject.internal.BindingImpl; import org.opensearch.common.inject.internal.Errors; import org.opensearch.common.inject.internal.ErrorsException; import org.opensearch.common.inject.internal.ExposedBindingImpl; import org.opensearch.common.inject.internal.InstanceBindingImpl; import org.opensearch.common.inject.internal.InternalFactory; import org.opensearch.common.inject.internal.LinkedBindingImpl; import org.opensearch.common.inject.internal.LinkedProviderBindingImpl; import org.opensearch.common.inject.internal.ProviderInstanceBindingImpl; import org.opensearch.common.inject.internal.ProviderMethod; import org.opensearch.common.inject.internal.Scoping; import org.opensearch.common.inject.internal.UntargettedBindingImpl; import org.opensearch.common.inject.spi.BindingTargetVisitor; import org.opensearch.common.inject.spi.ConstructorBinding; import org.opensearch.common.inject.spi.ConvertedConstantBinding; import org.opensearch.common.inject.spi.ExposedBinding; import org.opensearch.common.inject.spi.InjectionPoint; import org.opensearch.common.inject.spi.InstanceBinding; import org.opensearch.common.inject.spi.LinkedKeyBinding; import org.opensearch.common.inject.spi.PrivateElements; import org.opensearch.common.inject.spi.ProviderBinding; import org.opensearch.common.inject.spi.ProviderInstanceBinding; import org.opensearch.common.inject.spi.ProviderKeyBinding; import org.opensearch.common.inject.spi.UntargettedBinding; import java.util.ArrayList; import java.util.List; import java.util.Set; import static java.util.Collections.unmodifiableSet; import static org.opensearch.common.util.set.Sets.newHashSet; /** * Handles {@link Binder#bind} and {@link Binder#bindConstant} elements. * * @author crazybob@google.com (Bob Lee) * @author jessewilson@google.com (Jesse Wilson) * * @opensearch.internal */ class BindingProcessor extends AbstractProcessor { private final List creationListeners = new ArrayList<>(); private final Initializer initializer; private final List uninitializedBindings = new ArrayList<>(); BindingProcessor(Errors errors, Initializer initializer) { super(errors); this.initializer = initializer; } @Override public Boolean visit(Binding command) { final Object source = command.getSource(); if (Void.class.equals(command.getKey().getRawType())) { if (command instanceof ProviderInstanceBinding && ((ProviderInstanceBinding) command).getProviderInstance() instanceof ProviderMethod) { errors.voidProviderMethod(); } else { errors.missingConstantValues(); } return true; } final Key key = command.getKey(); Class rawType = key.getTypeLiteral().getRawType(); if (rawType == Provider.class) { errors.bindingToProvider(); return true; } validateKey(command.getSource(), command.getKey()); final Scoping scoping = Scopes.makeInjectable(((BindingImpl) command).getScoping(), injector, errors); command.acceptTargetVisitor(new BindingTargetVisitor() { @Override public Void visit(InstanceBinding binding) { Set injectionPoints = binding.getInjectionPoints(); T instance = binding.getInstance(); Initializable ref = initializer.requestInjection(injector, instance, source, injectionPoints); ConstantFactory factory = new ConstantFactory<>(ref); InternalFactory scopedFactory = Scopes.scope(key, injector, factory, scoping); putBinding(new InstanceBindingImpl<>(injector, key, source, scopedFactory, injectionPoints, instance)); return null; } @Override public Void visit(ProviderInstanceBinding binding) { Provider provider = binding.getProviderInstance(); Set injectionPoints = binding.getInjectionPoints(); Initializable> initializable = initializer.>requestInjection( injector, provider, source, injectionPoints ); InternalFactory factory = new InternalFactoryToProviderAdapter<>(initializable, source); InternalFactory scopedFactory = Scopes.scope(key, injector, factory, scoping); putBinding(new ProviderInstanceBindingImpl<>(injector, key, source, scopedFactory, scoping, provider, injectionPoints)); return null; } @Override public Void visit(ProviderKeyBinding binding) { Key> providerKey = binding.getProviderKey(); BoundProviderFactory boundProviderFactory = new BoundProviderFactory<>(injector, providerKey, source); creationListeners.add(boundProviderFactory); InternalFactory scopedFactory = Scopes.scope( key, injector, (InternalFactory) boundProviderFactory, scoping ); putBinding(new LinkedProviderBindingImpl<>(injector, key, source, scopedFactory, scoping, providerKey)); return null; } @Override public Void visit(LinkedKeyBinding binding) { Key linkedKey = binding.getLinkedKey(); if (key.equals(linkedKey)) { errors.recursiveBinding(); } FactoryProxy factory = new FactoryProxy<>(injector, key, linkedKey, source); creationListeners.add(factory); InternalFactory scopedFactory = Scopes.scope(key, injector, factory, scoping); putBinding(new LinkedBindingImpl<>(injector, key, source, scopedFactory, scoping, linkedKey)); return null; } @Override public Void visit(UntargettedBinding untargetted) { // Error: Missing implementation. // Example: bind(Date.class).annotatedWith(Red.class); // We can't assume abstract types aren't injectable. They may have an // @ImplementedBy annotation or something. if (key.hasAnnotationType()) { errors.missingImplementation(key); putBinding(invalidBinding(injector, key, source)); return null; } // This cast is safe after the preceding check. final BindingImpl binding; try { binding = injector.createUnitializedBinding(key, scoping, source, errors); putBinding(binding); } catch (ErrorsException e) { errors.merge(e.getErrors()); putBinding(invalidBinding(injector, key, source)); return null; } uninitializedBindings.add(new Runnable() { @Override public void run() { try { ((InjectorImpl) binding.getInjector()).initializeBinding(binding, errors.withSource(source)); } catch (ErrorsException e) { errors.merge(e.getErrors()); } } }); return null; } @Override public Void visit(ExposedBinding binding) { throw new IllegalArgumentException("Cannot apply a non-module element"); } @Override public Void visit(ConvertedConstantBinding binding) { throw new IllegalArgumentException("Cannot apply a non-module element"); } @Override public Void visit(ConstructorBinding binding) { throw new IllegalArgumentException("Cannot apply a non-module element"); } @Override public Void visit(ProviderBinding binding) { throw new IllegalArgumentException("Cannot apply a non-module element"); } }); return true; } @Override public Boolean visit(PrivateElements privateElements) { for (Key key : privateElements.getExposedKeys()) { bindExposed(privateElements, key); } return false; // leave the private elements for the PrivateElementsProcessor to handle } private void bindExposed(PrivateElements privateElements, Key key) { ExposedKeyFactory exposedKeyFactory = new ExposedKeyFactory<>(key, privateElements); creationListeners.add(exposedKeyFactory); putBinding(new ExposedBindingImpl<>(injector, privateElements.getExposedSource(key), key, exposedKeyFactory, privateElements)); } private void validateKey(Object source, Key key) { Annotations.checkForMisplacedScopeAnnotations(key.getRawType(), source, errors); } UntargettedBindingImpl invalidBinding(InjectorImpl injector, Key key, Object source) { return new UntargettedBindingImpl<>(injector, key, source); } public void initializeBindings() { for (Runnable initializer : uninitializedBindings) { initializer.run(); } } public void runCreationListeners() { for (CreationListener creationListener : creationListeners) { creationListener.notify(errors); } } private void putBinding(BindingImpl binding) { Key key = binding.getKey(); Class rawType = key.getRawType(); if (FORBIDDEN_TYPES.contains(rawType)) { errors.cannotBindToGuiceType(rawType.getSimpleName()); return; } Binding original = injector.state.getExplicitBinding(key); if (original != null && !isOkayDuplicate(original, binding)) { errors.bindingAlreadySet(key, original.getSource()); return; } // prevent the parent from creating a JIT binding for this key injector.state.parent().denylist(key); injector.state.putBinding(key, binding); } /** * We tolerate duplicate bindings only if one exposes the other. * * @param original the binding in the parent injector (candidate for an exposing binding) * @param binding the binding to check (candidate for the exposed binding) */ private boolean isOkayDuplicate(Binding original, BindingImpl binding) { if (original instanceof ExposedBindingImpl) { ExposedBindingImpl exposed = (ExposedBindingImpl) original; InjectorImpl exposedFrom = (InjectorImpl) exposed.getPrivateElements().getInjector(); return (exposedFrom == binding.getInjector()); } return false; } // It's unfortunate that we have to maintain a denylist of specific // classes, but we can't easily block the whole package because of // all our unit tests. private static final Set> FORBIDDEN_TYPES = unmodifiableSet( newHashSet( AbstractModule.class, Binder.class, Binding.class, Injector.class, Key.class, MembersInjector.class, Module.class, Provider.class, Scope.class, TypeLiteral.class ) ); // TODO(jessewilson): fix BuiltInModule, then add Stage /** * A listener for a process creation * * @opensearch.internal */ interface CreationListener { void notify(Errors errors); } }