From 34b9d4f246c0e91642fa10460a601324c5c66906 Mon Sep 17 00:00:00 2001 From: Jedediah Smith Date: Mon, 6 Feb 2017 07:05:31 -0500 Subject: [PATCH] Fix Connectables connecting in the wrong order --- .../tc/oc/api/annotations/ApiRequired.java | 12 ----- .../tc/oc/api/connectable/Connectable.java | 15 ++++-- .../oc/api/connectable/ConnectableBinder.java | 10 ---- .../api/connectable/ConnectablesManifest.java | 15 ++++-- .../java/tc/oc/api/connectable/Connector.java | 52 ++++++++++++------- .../java/tc/oc/api/model/ModelBinder.java | 9 ++-- .../java/tc/oc/api/queue/QueueClient.java | 20 ++++--- .../java/tc/oc/api/queue/QueueManifest.java | 20 ++----- .../api/minecraft/MinecraftApiManifest.java | 6 +-- .../tc/oc/pgm/api/EngagementMatchModule.java | 2 - .../pgm/api/MatchPublishingMatchModule.java | 2 - .../ParticipationPublishingMatchModule.java | 3 +- .../pgm/match/inject/MatchModuleManifest.java | 9 ---- .../pgm/stats/DeathPublishingMatchModule.java | 3 +- .../stats/ObjectivePublishingMatchModule.java | 3 +- .../tc/oc/commons/core/inject/Binders.java | 15 ++++++ 16 files changed, 95 insertions(+), 101 deletions(-) delete mode 100644 API/api/src/main/java/tc/oc/api/annotations/ApiRequired.java delete mode 100644 API/api/src/main/java/tc/oc/api/connectable/ConnectableBinder.java diff --git a/API/api/src/main/java/tc/oc/api/annotations/ApiRequired.java b/API/api/src/main/java/tc/oc/api/annotations/ApiRequired.java deleted file mode 100644 index 9b97d13..0000000 --- a/API/api/src/main/java/tc/oc/api/annotations/ApiRequired.java +++ /dev/null @@ -1,12 +0,0 @@ -package tc.oc.api.annotations; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -/** - * Generic annotation for something that requires an API connection, - * and should be ommitted or cause an error if not connected. - * - * (maybe we should make that distinction explicit?) - */ -@Retention(RetentionPolicy.RUNTIME) -public @interface ApiRequired {} diff --git a/API/api/src/main/java/tc/oc/api/connectable/Connectable.java b/API/api/src/main/java/tc/oc/api/connectable/Connectable.java index fda2cd2..d233dca 100644 --- a/API/api/src/main/java/tc/oc/api/connectable/Connectable.java +++ b/API/api/src/main/java/tc/oc/api/connectable/Connectable.java @@ -2,16 +2,21 @@ package tc.oc.api.connectable; import java.io.IOException; +import com.google.inject.binder.ScopedBindingBuilder; import tc.oc.minecraft.api.event.Activatable; -import tc.oc.commons.core.plugin.PluginFacet; /** - * Service that needs to be connected and disconnected along with the API. + * Service that needs to be connected and disconnected along with the API * - * Use a {@link ConnectableBinder} to register these. + * Registration happens automatically the first time any {@link Connectable} + * instance is provisioned through Guice. If this happens before or during + * the connection process, the instance will be connected in the same order + * that it was provisioned, with respect to other {@link Connectable}s. * - * TODO: This should probably extend {@link PluginFacet}, - * but to do that, API needs to be able to find the services bound in other plugins. + * If a new {@link Connectable} instance is provisioned after the connection + * phase is complete, an exception is thrown. To ensure that a {@link Connectable} + * is provisioned in time to be connected, it is usually scoped with + * {@link ScopedBindingBuilder#asEagerSingleton()} */ public interface Connectable extends Activatable { default void connect() throws IOException {}; diff --git a/API/api/src/main/java/tc/oc/api/connectable/ConnectableBinder.java b/API/api/src/main/java/tc/oc/api/connectable/ConnectableBinder.java deleted file mode 100644 index 2bf102e..0000000 --- a/API/api/src/main/java/tc/oc/api/connectable/ConnectableBinder.java +++ /dev/null @@ -1,10 +0,0 @@ -package tc.oc.api.connectable; - -import com.google.inject.Binder; -import tc.oc.commons.core.inject.SetBinder; - -public class ConnectableBinder extends SetBinder { - public ConnectableBinder(Binder binder) { - super(binder); - } -} diff --git a/API/api/src/main/java/tc/oc/api/connectable/ConnectablesManifest.java b/API/api/src/main/java/tc/oc/api/connectable/ConnectablesManifest.java index 5f5e7a8..6f03a49 100644 --- a/API/api/src/main/java/tc/oc/api/connectable/ConnectablesManifest.java +++ b/API/api/src/main/java/tc/oc/api/connectable/ConnectablesManifest.java @@ -1,14 +1,21 @@ package tc.oc.api.connectable; +import javax.inject.Provider; + import tc.oc.commons.core.inject.HybridManifest; -import tc.oc.commons.core.plugin.PluginFacetBinder; +import tc.oc.minecraft.api.event.ListenerBinder; public class ConnectablesManifest extends HybridManifest { @Override protected void configure() { - bindAndExpose(Connector.class); - new PluginFacetBinder(binder()) - .add(Connector.class); + bind(Connector.class); + new ListenerBinder(binder()) + .bindListener().to(Connector.class); + + final Provider connectorProvider = getProvider(Connector.class); + publicBinder().bindProvisionSubtypesOfListener(Connectable.class, provision -> { + connectorProvider.get().register(provision.provision()); + }); } } diff --git a/API/api/src/main/java/tc/oc/api/connectable/Connector.java b/API/api/src/main/java/tc/oc/api/connectable/Connector.java index 57bfca8..c04dc3c 100644 --- a/API/api/src/main/java/tc/oc/api/connectable/Connector.java +++ b/API/api/src/main/java/tc/oc/api/connectable/Connector.java @@ -1,6 +1,10 @@ package tc.oc.api.connectable; import java.io.IOException; +import java.util.Collections; +import java.util.Deque; +import java.util.IdentityHashMap; +import java.util.LinkedList; import java.util.Set; import java.util.logging.Logger; import javax.inject.Inject; @@ -8,28 +12,36 @@ import javax.inject.Singleton; import tc.oc.commons.core.exception.ExceptionHandler; import tc.oc.commons.core.logging.Loggers; -import tc.oc.commons.core.plugin.PluginFacet; import tc.oc.commons.core.util.ExceptionUtils; +import tc.oc.minecraft.api.event.Enableable; import static com.google.common.base.Preconditions.checkState; -import static tc.oc.commons.core.IterableUtils.reverseForEach; -import static tc.oc.commons.core.exception.LambdaExceptionUtils.rethrowConsumer; @Singleton -public class Connector implements PluginFacet { +class Connector implements Enableable { protected final Logger logger; private final ExceptionHandler exceptionHandler; - private final Set services; - private boolean connected; + private final Set registered = Collections.newSetFromMap(new IdentityHashMap<>()); + private final Deque pending = new LinkedList<>(); + private final Deque connected = new LinkedList<>(); + private boolean finishedConnecting; - @Inject - Connector(Loggers loggers, ExceptionHandler exceptionHandler, Set services) { + @Inject Connector(Loggers loggers, ExceptionHandler exceptionHandler) { this.exceptionHandler = exceptionHandler; - this.services = services; this.logger = loggers.get(getClass()); } + void register(Connectable connectable) { + if(registered.add(connectable)) { + if(finishedConnecting) { + throw new IllegalStateException("Tried to provision a " + Connectable.class.getSimpleName() + + " when already connected"); + } + pending.add(connectable); + } + } + private void connect(Connectable service) throws IOException { if(service.isActive()) { logger.fine(() -> "Connecting " + service.getClass().getName()); @@ -44,23 +56,25 @@ public class Connector implements PluginFacet { } } - public boolean isConnected() { - return connected; - } - @Override public void enable() { - checkState(!connected, "already connected"); - connected = true; + checkState(!finishedConnecting, "already connected"); logger.fine(() -> "Connecting all services"); - ExceptionUtils.propagate(() -> services.forEach(rethrowConsumer(this::connect))); + for(;;) { + final Connectable connectable = pending.poll(); + if(connectable == null) break; + ExceptionUtils.propagate(() -> connect(connectable)); + connected.push(connectable); + } + finishedConnecting = true; } @Override public void disable() { - checkState(connected, "not connected"); - connected = false; + checkState(finishedConnecting, "not connected"); logger.fine(() -> "Disconnecting all services"); - reverseForEach(services, service -> exceptionHandler.run(() -> disconnect(service))); + while(!connected.isEmpty()) { + exceptionHandler.run(() -> disconnect(connected.pop())); + } } } diff --git a/API/api/src/main/java/tc/oc/api/model/ModelBinder.java b/API/api/src/main/java/tc/oc/api/model/ModelBinder.java index 546378f..c263b73 100644 --- a/API/api/src/main/java/tc/oc/api/model/ModelBinder.java +++ b/API/api/src/main/java/tc/oc/api/model/ModelBinder.java @@ -4,7 +4,6 @@ import java.util.Map; import java.util.Set; import javax.inject.Singleton; -import com.google.inject.Binder; import com.google.inject.Provides; import com.google.inject.TypeLiteral; import com.google.inject.binder.LinkedBindingBuilder; @@ -12,8 +11,8 @@ import com.google.inject.multibindings.Multibinder; import com.google.inject.multibindings.OptionalBinder; import tc.oc.api.docs.virtual.Model; import tc.oc.api.docs.virtual.PartialModel; -import tc.oc.api.connectable.ConnectableBinder; import tc.oc.api.queue.QueueQueryService; +import tc.oc.commons.core.inject.Binders; import tc.oc.commons.core.inject.KeyedManifest; import tc.oc.commons.core.inject.SingletonManifest; import tc.oc.commons.core.reflect.ResolvableType; @@ -29,7 +28,7 @@ public class ModelBinder implements Mod private final TypeLiteral M; private final TypeLiteral

P; - private final Binder binder; + private final Binders binder; private final Multibinder metas; private final OptionalBinder> queryServiceBinder; private final OptionalBinder> updateServiceBinder; @@ -53,7 +52,7 @@ public class ModelBinder implements Mod } private ModelBinder(ProtectedBinder protectedBinder, TypeLiteral M, TypeLiteral

P) { - this.binder = protectedBinder.publicBinder(); + this.binder = Binders.wrap(protectedBinder.publicBinder()); this.M = M; this.P = P; @@ -68,7 +67,7 @@ public class ModelBinder implements Mod } public LinkedBindingBuilder> bindStore() { - new ConnectableBinder(binder).addBinding().to(ModelStore(M)); + binder.provisionEagerly(ModelStore(M)); new SuspendableBinder(binder).addBinding().to(ModelStore(M)); return storeBinder.setBinding(); } diff --git a/API/api/src/main/java/tc/oc/api/queue/QueueClient.java b/API/api/src/main/java/tc/oc/api/queue/QueueClient.java index 800735b..cd71b03 100644 --- a/API/api/src/main/java/tc/oc/api/queue/QueueClient.java +++ b/API/api/src/main/java/tc/oc/api/queue/QueueClient.java @@ -164,7 +164,7 @@ public class QueueClient implements Connectable { } try { - this.channel.basicPublish(exchange.name(), + getChannel().basicPublish(exchange.name(), publish.routingKey(), publish.mandatory(), publish.immediate(), @@ -220,16 +220,22 @@ public class QueueClient implements Connectable { @Override public void connect() throws IOException { - // create connection and channel - logger.info("Connecting to AMQP API at " + Joiners.onCommaSpace.join(config.getAddresses())); - this.connection = this.createConnectionFactory().newConnection(this.config.getAddresses().toArray(new Address[0])); - this.channel = this.connection.createChannel(); + if(config.getAddresses().isEmpty()) { + logger.warning("Skipping AMQP connection because no addresses are configured"); + } else { + logger.info("Connecting to AMQP API at " + Joiners.onCommaSpace.join(config.getAddresses())); + this.connection = this.createConnectionFactory().newConnection(this.config.getAddresses().toArray(new Address[0])); + this.channel = this.connection.createChannel(); + } } @Override public void disconnect() throws IOException { ExecutorUtils.shutdownImpatiently(executorService, logger, SHUTDOWN_TIMEOUT); - this.channel.close(); - this.connection.close(); + + if(channel != null) { + channel.close(); + connection.close(); + } } } diff --git a/API/api/src/main/java/tc/oc/api/queue/QueueManifest.java b/API/api/src/main/java/tc/oc/api/queue/QueueManifest.java index 1fc1342..facc75a 100644 --- a/API/api/src/main/java/tc/oc/api/queue/QueueManifest.java +++ b/API/api/src/main/java/tc/oc/api/queue/QueueManifest.java @@ -1,6 +1,5 @@ package tc.oc.api.queue; -import tc.oc.api.connectable.ConnectableBinder; import tc.oc.api.message.MessageQueue; import tc.oc.commons.core.inject.HybridManifest; import tc.oc.minecraft.suspend.SuspendableBinder; @@ -12,24 +11,15 @@ public class QueueManifest extends HybridManifest { bindAndExpose(QueueClientConfiguration.class) .to(QueueClientConfigurationImpl.class); - bindAndExpose(QueueClient.class); - bindAndExpose(Exchange.Direct.class); - bindAndExpose(Exchange.Fanout.class); - bindAndExpose(Exchange.Topic.class); - bindAndExpose(PrimaryQueue.class); + bindAndExpose(QueueClient.class).asEagerSingleton(); + bindAndExpose(Exchange.Direct.class).asEagerSingleton(); + bindAndExpose(Exchange.Fanout.class).asEagerSingleton(); + bindAndExpose(Exchange.Topic.class).asEagerSingleton(); + bindAndExpose(PrimaryQueue.class).asEagerSingleton(); publicBinder().forOptional(MessageQueue.class) .setBinding().to(PrimaryQueue.class); - // These will connect in the order listed here. - // TODO: figure out the order from their dependencies. - final ConnectableBinder services = new ConnectableBinder(publicBinder()); - services.addBinding().to(QueueClient.class); - services.addBinding().to(Exchange.Direct.class); - services.addBinding().to(Exchange.Fanout.class); - services.addBinding().to(Exchange.Topic.class); - services.addBinding().to(PrimaryQueue.class); - final SuspendableBinder suspendables = new SuspendableBinder(publicBinder()); suspendables.addBinding().to(PrimaryQueue.class); } diff --git a/API/minecraft/src/main/java/tc/oc/api/minecraft/MinecraftApiManifest.java b/API/minecraft/src/main/java/tc/oc/api/minecraft/MinecraftApiManifest.java index 323c0ab..f45148d 100644 --- a/API/minecraft/src/main/java/tc/oc/api/minecraft/MinecraftApiManifest.java +++ b/API/minecraft/src/main/java/tc/oc/api/minecraft/MinecraftApiManifest.java @@ -2,7 +2,6 @@ package tc.oc.api.minecraft; import com.google.inject.Provides; import tc.oc.api.config.ApiConfiguration; -import tc.oc.api.connectable.ConnectableBinder; import tc.oc.api.docs.Server; import tc.oc.api.docs.virtual.ServerDoc; import tc.oc.api.minecraft.config.MinecraftApiConfiguration; @@ -24,9 +23,6 @@ public final class MinecraftApiManifest extends HybridManifest { @Override protected void configure() { bind(ServerDoc.Identity.class).to(Server.class); - - new ConnectableBinder(binder()) - .addBinding().to(MinecraftServiceImpl.class); } @Provides Server localServer(MinecraftService minecraftService) { @@ -53,6 +49,6 @@ public final class MinecraftApiManifest extends HybridManifest { bindAndExpose(MinecraftApiConfiguration.class).to(MinecraftApiConfigurationImpl.class); bindAndExpose(MinecraftService.class).to(MinecraftServiceImpl.class); - bindAndExpose(MinecraftServiceImpl.class); // Needs to be exposed so it can be registered as a connectable service + bind(MinecraftServiceImpl.class).asEagerSingleton(); } } diff --git a/PGM/src/main/java/tc/oc/pgm/api/EngagementMatchModule.java b/PGM/src/main/java/tc/oc/pgm/api/EngagementMatchModule.java index 01c9f08..5bd50a8 100644 --- a/PGM/src/main/java/tc/oc/pgm/api/EngagementMatchModule.java +++ b/PGM/src/main/java/tc/oc/pgm/api/EngagementMatchModule.java @@ -19,7 +19,6 @@ import net.md_5.bungee.api.chat.TranslatableComponent; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; -import tc.oc.api.annotations.ApiRequired; import tc.oc.api.bukkit.users.BukkitUserStore; import tc.oc.api.docs.PlayerId; import tc.oc.api.docs.virtual.EngagementDoc; @@ -54,7 +53,6 @@ import tc.oc.pgm.victory.VictoryMatchModule; /** * Responsible for creating/updating {@link EngagementDoc}s. */ -@ApiRequired @ListenerScope(MatchScope.LOADED) public class EngagementMatchModule extends MatchModule implements Listener { diff --git a/PGM/src/main/java/tc/oc/pgm/api/MatchPublishingMatchModule.java b/PGM/src/main/java/tc/oc/pgm/api/MatchPublishingMatchModule.java index ac1abed..03806c0 100644 --- a/PGM/src/main/java/tc/oc/pgm/api/MatchPublishingMatchModule.java +++ b/PGM/src/main/java/tc/oc/pgm/api/MatchPublishingMatchModule.java @@ -5,7 +5,6 @@ import javax.inject.Inject; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; -import tc.oc.api.annotations.ApiRequired; import tc.oc.api.docs.virtual.MapDoc; import tc.oc.api.docs.virtual.MatchDoc; import tc.oc.api.docs.virtual.ServerDoc; @@ -25,7 +24,6 @@ import tc.oc.pgm.match.MatchModule; import tc.oc.pgm.match.MatchScope; import tc.oc.pgm.teams.events.TeamResizeEvent; -@ApiRequired @ListenerScope(MatchScope.LOADED) public class MatchPublishingMatchModule extends MatchModule implements Listener { class Update implements ServerDoc.MatchStatusUpdate { diff --git a/PGM/src/main/java/tc/oc/pgm/api/ParticipationPublishingMatchModule.java b/PGM/src/main/java/tc/oc/pgm/api/ParticipationPublishingMatchModule.java index 280d977..f0f6f2e 100644 --- a/PGM/src/main/java/tc/oc/pgm/api/ParticipationPublishingMatchModule.java +++ b/PGM/src/main/java/tc/oc/pgm/api/ParticipationPublishingMatchModule.java @@ -9,7 +9,7 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import java.time.Instant; -import tc.oc.api.annotations.ApiRequired; + import tc.oc.api.bukkit.users.BukkitUserStore; import tc.oc.api.docs.Participation; import tc.oc.api.docs.Server; @@ -26,7 +26,6 @@ import tc.oc.pgm.match.MatchScope; import tc.oc.pgm.match.Party; import tc.oc.pgm.teams.Team; -@ApiRequired @ListenerScope(MatchScope.LOADED) public class ParticipationPublishingMatchModule extends MatchModule implements Listener { diff --git a/PGM/src/main/java/tc/oc/pgm/match/inject/MatchModuleManifest.java b/PGM/src/main/java/tc/oc/pgm/match/inject/MatchModuleManifest.java index 964ff9c..6bbb7ee 100644 --- a/PGM/src/main/java/tc/oc/pgm/match/inject/MatchModuleManifest.java +++ b/PGM/src/main/java/tc/oc/pgm/match/inject/MatchModuleManifest.java @@ -7,8 +7,6 @@ import javax.inject.Provider; import com.google.inject.TypeLiteral; import org.bukkit.event.Listener; -import tc.oc.api.connectable.Connector; -import tc.oc.api.annotations.ApiRequired; import tc.oc.commons.core.reflect.Types; import tc.oc.pgm.match.Match; import tc.oc.pgm.match.MatchModule; @@ -27,15 +25,12 @@ import tc.oc.pgm.module.ModuleManifest; */ public abstract class MatchModuleManifest extends ModuleManifest { - protected final boolean apiRequired; - protected MatchModuleManifest() { this(null); } protected MatchModuleManifest(@Nullable TypeLiteral type) { super(type); - this.apiRequired = rawType.isAnnotationPresent(ApiRequired.class); } @Override @@ -49,13 +44,9 @@ public abstract class MatchModuleManifest extends ModuleM } @Inject protected Provider matchProvider; - @Inject private Connector connector; @Override protected final Optional provisionModuleWithoutDependencies() throws ModuleLoadException { - if(apiRequired && !connector.isConnected()) { - return Optional.empty(); - } return provisionModuleWithoutFiltering() .filter(MatchModule::shouldLoad); } diff --git a/PGM/src/main/java/tc/oc/pgm/stats/DeathPublishingMatchModule.java b/PGM/src/main/java/tc/oc/pgm/stats/DeathPublishingMatchModule.java index b1612ec..f8fbfde 100644 --- a/PGM/src/main/java/tc/oc/pgm/stats/DeathPublishingMatchModule.java +++ b/PGM/src/main/java/tc/oc/pgm/stats/DeathPublishingMatchModule.java @@ -10,7 +10,7 @@ import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import java.time.Duration; import java.time.Instant; -import tc.oc.api.annotations.ApiRequired; + import tc.oc.api.docs.Server; import tc.oc.api.docs.virtual.DeathDoc; import tc.oc.api.model.BatchUpdater; @@ -37,7 +37,6 @@ import tc.oc.pgm.tracker.damage.SpleefInfo; import tc.oc.pgm.tracker.damage.TNTInfo; import tc.oc.pgm.tracker.damage.TrackerInfo; -@ApiRequired @ListenerScope(MatchScope.LOADED) public class DeathPublishingMatchModule extends MatchModule implements Listener { diff --git a/PGM/src/main/java/tc/oc/pgm/stats/ObjectivePublishingMatchModule.java b/PGM/src/main/java/tc/oc/pgm/stats/ObjectivePublishingMatchModule.java index a7d07cf..a004a0f 100644 --- a/PGM/src/main/java/tc/oc/pgm/stats/ObjectivePublishingMatchModule.java +++ b/PGM/src/main/java/tc/oc/pgm/stats/ObjectivePublishingMatchModule.java @@ -4,7 +4,7 @@ import org.bukkit.Location; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import java.time.Instant; -import tc.oc.api.annotations.ApiRequired; + import tc.oc.api.docs.Objective; import tc.oc.api.docs.Server; import tc.oc.api.model.IdFactory; @@ -27,7 +27,6 @@ import tc.oc.pgm.wool.PlayerWoolPlaceEvent; import javax.annotation.Nullable; import javax.inject.Inject; -@ApiRequired @ListenerScope(MatchScope.LOADED) public class ObjectivePublishingMatchModule extends MatchModule implements Listener { diff --git a/Util/core/src/main/java/tc/oc/commons/core/inject/Binders.java b/Util/core/src/main/java/tc/oc/commons/core/inject/Binders.java index 6aa208c..a1495e2 100644 --- a/Util/core/src/main/java/tc/oc/commons/core/inject/Binders.java +++ b/Util/core/src/main/java/tc/oc/commons/core/inject/Binders.java @@ -4,6 +4,7 @@ import java.lang.annotation.Annotation; import java.util.function.Consumer; import java.util.function.Predicate; import java.util.function.UnaryOperator; +import javax.inject.Inject; import com.google.inject.Binder; import com.google.inject.Binding; @@ -21,6 +22,8 @@ import com.google.inject.spi.Elements; import com.google.inject.spi.ProvisionListener; import com.google.inject.spi.TypeEncounter; import com.google.inject.spi.TypeListener; +import tc.oc.commons.core.reflect.ResolvableType; +import tc.oc.commons.core.reflect.TypeArgument; import tc.oc.commons.core.reflect.TypeParameter; import tc.oc.commons.core.reflect.TypeResolver; import tc.oc.commons.core.reflect.Types; @@ -161,6 +164,18 @@ public interface Binders extends ForwardingBinder { return membersInjector(TypeLiteral.get(type)); } + class EagerProvisioner { + @Inject EagerProvisioner(T t) {} + } + + default void provisionEagerly(Key key) { + bind(key.ofType(new ResolvableType>(){}.with(new TypeArgument(key.getTypeLiteral()){}))) + .asEagerSingleton(); + } + + default void provisionEagerly(TypeLiteral type) { provisionEagerly(Key.get(type)); } + default void provisionEagerly(Class type) { provisionEagerly(Key.get(type)); } + @Override default Binders withSource(Object source) { return wrap(ForwardingBinder.super.withSource(source));