Reputation: 895
I'm getting:
C:\Users\Gieted\Projekty\CraftIt\runtime\build\tmp\kapt3\stubs\main\org\craftit\runtime\MainComponent.java:8: error: [Dagger/MissingBinding] java.lang.ClassLoader cannot be provided without an @Provides-annotated method.
public abstract interface MainComponent {
^
A binding with matching key exists in component: org.craftit.runtime.server.ServerComponent
java.lang.ClassLoader is injected at
org.craftit.runtime.resources.packets.converters.DisplayMessageConverter(�, classLoader, �)
org.craftit.runtime.resources.packets.converters.DisplayMessageConverter is injected at
org.craftit.runtime.resources.packets.converters.PacketConverter(�, displayMessageConverter)
org.craftit.runtime.resources.packets.converters.PacketConverter is injected at
org.craftit.runtime.Bridge(�, packetConverter, �)
org.craftit.runtime.Bridge is injected at
org.craftit.runtime.server.initializers.VanillaServerInitializer(bridge, �)
org.craftit.runtime.server.initializers.VanillaServerInitializer is injected at
org.craftit.runtime.server.ServerModule.nativeServerInitializer(to)
org.craftit.runtime.server.initializers.NativeServerInitializer is requested at
org.craftit.runtime.server.ServerComponent.nativeServerInitializer() [org.craftit.runtime.MainComponent ? org.craftit.runtime.server.ServerComponent]
It is also requested at:
org.craftit.runtime.resources.entities.player.ChatType(�, classLoader)
org.craftit.runtime.resources.entities.player.NativeConnectorImpl(�, classLoader)
org.craftit.runtime.resources.packets.converters.SendChatMessageConverter(�, classLoader)
org.craftit.runtime.text.NativeColorFactory(�, classLoader)
org.craftit.runtime.text.StringTextComponentFactory(�, classLoader, �)
org.craftit.runtime.text.StyleFactory(�, classLoader, �)
The following other entry points also depend on it:
org.craftit.runtime.server.ServerComponent.entityRegistry() [org.craftit.runtime.MainComponent ? org.craftit.runtime.server.ServerComponent]
So "A binding with matching key exists in component: org.craftit.runtime.server.ServerComponent
" suggests that the requested binding is found in ServerComponent
, but at the same time is requested at org.craftit.runtime.server.ServerComponent.nativeServerInitializer()
tells that the missing binding comes from ServerComponent
, which doesn't make any sense. Can someone explain to me what's going on, and where should I search for errors?
@ServerScope
@Subcomponent(modules = [ServerModule::class])
interface ServerComponent {
fun pluginLoader(): PluginLoader
fun entityRegistry(): EntityRegistry
fun nativeServerInitializer(): NativeServerInitializer
fun commandRegistry(): CommandRegistry
fun pluginRegistry(): PluginRegistry
@Subcomponent.Factory
interface Factory {
fun create(@BindsInstance server: Server): ServerComponent
}
}
@Singleton
@Component(modules = [MainModule::class])
interface MainComponent {
@Named("new")
fun server(): Server
@Component.Factory
interface Factory {
fun create(@BindsInstance configuration: Configuration): MainComponent
}
}
class VanillaServer @Inject constructor(
serverComponentFactory: ServerComponent.Factory
) : Server {
override val entities: EntityRegistry
override val commands: CommandRegistry
override val plugins: PluginRegistry
private val pluginLoader: PluginLoader
private val nativeServerInitializer: NativeServerInitializer
init {
val component = serverComponentFactory.create(this)
pluginLoader = component.pluginLoader()
entities = component.entityRegistry()
nativeServerInitializer = component.nativeServerInitializer()
commands = component.commandRegistry()
plugins = component.pluginRegistry()
}
override fun start() {
nativeServerInitializer.startServer()
pluginLoader.loadPlugins()
plugins.forEach { it.enable() }
}
}
@Module
abstract class ServerModule {
companion object {
@Provides
@ServerScope
fun classLoader(configuration: Configuration): ClassLoader =
URLClassLoader(arrayOf(configuration.serverFile.toURI().toURL()))
}
@Binds
@ServerScope
abstract fun entityRegistry(to: VanillaEntityRegistry): EntityRegistry
@Binds
@ServerScope
abstract fun nativeServerInitializer(to: VanillaServerInitializer): NativeServerInitializer
}
Upvotes: 2
Views: 1150
Reputation: 95704
The real problem is far on the right side of your first line:
MainComponent.java:8: error: [Dagger/MissingBinding]
java.lang.ClassLoader
cannot be provided without an@Provides
-annotated method.
You can see it provided in ServerModule, but you're getting the message because something that is supposedly accessible from the MainComponent could not get to the ClassLoader you provide in the ServerComponent. This makes sense: Bindings from MainComponent are accessible in ServerComponent, but bindings in ServerComponent are not accessible in MainComponent unless retrieved through a specific instance of ServerComponent that you create through ServerComponent's factory.
This is also why your solution worked:
VanillaServerInitializer
andBridge
are server scoped,PacketConverter
andDisplayMessageConverter
were unscoped. If I try adding@ServerScope
toPacketConverter
I'm getting:error: [Dagger/IncompatiblyScopedBindings] org.craftit.runtime.MainComponent scoped with @Singleton may not reference bindings with different scopes
. This is weird, becausePacketConverter
is never created byMainComponent
.
Setting either singleton or server scope on every binding fixed the issue. But why have it happened in the first place?
Since you'd had PacketConverter and DisplayMessageConverter unscoped, Dagger tried to implement them in your MainComponent where a ClassLoader could not be supplied. (I also would have expected PacketConverter to be implemented in ServerComponent if at all possible, but what you're describing would be more consistent with it being accessed somewhere transitively from MainComponent's bindings, either through @Named("new") Server
or a binding in the MainModule you've omitted.)
In any case, as long as you only try to access your bindings from @ServerScoped
and unscoped classes, never from @Singleton
or MainComponent-accessible classes, you should be fine.
Upvotes: 3