Motivation
We maintain a reactive web framework called Armeria (https://armeria.dev). It allows a user to implement a reactive web application using the Reactive Streams implementation of their choice. A user can choose from Armeria's own Reactive Streams implementation, Reactor and RxJava. Therefore, we don't have any mandatory dependency on Reactor and RxJava to keep our dependencies lean and stay unopinionated, and we want to keep it this way.
When a user writes their application using Armeria and Reactor, Armeria needs to tell Reactor that its event loop threads are non-blocking. Reactor currently determines whether a thread is non-blocking or not using Schedulers.isInNonBlockingThread() and Schedulers.isNonBlockingThread() (here).
It means, for Armeria to mark a thread as non-blocking, Armeria needs to make its event loop thread class implement the NonBlocking interface, e.g.:
public class ArmeriaEventLoopThread extends Thread implements NonBlocking { ... }
However, because Armeria has no mandatory dependency on Reactor, JVM will fail to load ArmeriaEventLoopThread when Reactor is not available in the classpath, if we introduce NonBlocking into our class hierarchy.
Desired solution
Provide an alternative way to mark a thread (or thread class) as non-blocking, so that Armeria team can define a non-blocking thread without introducing mandatory runtime dependency on reactor-core. There are a few options to solve this:
- Provide a small separate JAR that contains
NonBlocking (and potentially other tagging interfaces) and nothing more.
- Straightforward change with no additional overhead and a minimal dependency size increase.
- However, it requires reorganization in your codebase.
reactor-core needs to depend on the new JAR, which may make things a little bit more complicated?
- Java SPI
- Let a user define an SPI implementation that's loaded by Reactor in runtime.
- This will work because: 1) our threads will not depend on any Reactor classes and 2) the SPI implementation will be loaded only when Reactor is initialized.
- However, it will perhaps increase the overhead.
- Provide API to register a subclass of
Thread as non-blocking.
- Let a user call a new Reactor API that registers the given
Thread subclass to the list of non-blocking thread types.
- This is similar to Java SPI option, but Armeria team will need to call this API using reflection. (We're OK doing that though.)
- Same overhead concern.
- Check the class name
- Just check if the FQCN contains
NonBlocking, as well as the instanceof NonBlocking check.
- A little bit of overhead concern.
Considered alternatives
- As an alternative, we defined the identical duplicate
NonBlocking interface in our codebase. This works because NonBlocking is just a tag interface and thus having a duplicate class in the classpath won't hurt. However, this doesn't work with Java Modules, which never allows having the same classes in more than one module.
- We could simply add
reactor-core as a mandatory dependency, but our users will not be happy with it because it'd make their dependency tree bigger even if they don't use Reactor.
Motivation
We maintain a reactive web framework called Armeria (https://armeria.dev). It allows a user to implement a reactive web application using the Reactive Streams implementation of their choice. A user can choose from Armeria's own Reactive Streams implementation, Reactor and RxJava. Therefore, we don't have any mandatory dependency on Reactor and RxJava to keep our dependencies lean and stay unopinionated, and we want to keep it this way.
When a user writes their application using Armeria and Reactor, Armeria needs to tell Reactor that its event loop threads are non-blocking. Reactor currently determines whether a thread is non-blocking or not using
Schedulers.isInNonBlockingThread()andSchedulers.isNonBlockingThread()(here).It means, for Armeria to mark a thread as non-blocking, Armeria needs to make its event loop thread class implement the
NonBlockinginterface, e.g.:However, because Armeria has no mandatory dependency on Reactor, JVM will fail to load
ArmeriaEventLoopThreadwhen Reactor is not available in the classpath, if we introduceNonBlockinginto our class hierarchy.Desired solution
Provide an alternative way to mark a thread (or thread class) as non-blocking, so that Armeria team can define a non-blocking thread without introducing mandatory runtime dependency on
reactor-core. There are a few options to solve this:NonBlocking(and potentially other tagging interfaces) and nothing more.reactor-coreneeds to depend on the new JAR, which may make things a little bit more complicated?Threadas non-blocking.Threadsubclass to the list of non-blocking thread types.NonBlocking, as well as theinstanceof NonBlockingcheck.Considered alternatives
NonBlockinginterface in our codebase. This works becauseNonBlockingis just a tag interface and thus having a duplicate class in the classpath won't hurt. However, this doesn't work with Java Modules, which never allows having the same classes in more than one module.reactor-coreas a mandatory dependency, but our users will not be happy with it because it'd make their dependency tree bigger even if they don't use Reactor.