Skip to content

Add third-party integrations to the quantities library

Floris Westerman requested to merge fw/integrations into develop

Solved #2 (closed) and #3 (closed)

As discussed in the issue and in other MRs, it seems to be useful to have some kind of "integrations" between the quantities library and third-party libraries like Jackson and Smallrye config. The reason why I initially was hesitant about this, is that this introduces transient dependencies at runtime that influence all users of this library - thus causing runtime bloat.

I can see two main ways around this "bloat":

  • Create a separate "integrations" library that augments the quantities library with the third-party integrations. Then a user of quantities that also uses Quarkus can include the quarkus-integrations library to enjoy automatic configuration, for example. However, this still ties all transient dependencies together: the inclusion of integrations for Smallrye config would also necessitate including Quarkus for the JSON parsing. This is how these integrations are often done, but does not fully mitigate the bloat. Alternatively, we could create separate libraries for each possible integration. However, this will really bloat our namespaces and complicate the discovery of these integrations.
  • Lie to JVM. The trick here is to tell Gradle that surely, really, pinky promise we only use these third-party libraries during compilation, and really really not during runtime or in our public API. Then Gradle will happily consume our library in other projects without bringing in all the transient dependencies. The only issue with this: as soon as the user touches the integration code, compilation breaks or the runtime crashes.

This MR still goes for the second route, despite the potential risk for crashes. This (IMO) is justified because the integrations for the quantities library are all typically not touched by the user. The Smallrye converter is automatically registered through META-INF, and when using full Quarkus, the Jackson deserializer is also automatically injected through the ObjectMapperCustomizer. Only when using Jackson without Quarkus, a manual call to registerQuantitiesModule is made, but that is only possible if the user already uses Jackson and has an instance of ObjectMapper - thus then the code is still safe.

I tested this approach by creating a separate module that included the quantities library but not any of the transient dependencies. I could still use ByteSize like normal, without any issues during testing or compilation. Only as soon as I tried to instantiate some of these helper classes in the integration library, I would get compilation and runtime issues. But then again - these helper classes are typically not instantiated by hand.

The benefits of this route are that the binary is kept small, there are no undesirable transient dependencies, and there is no need to explicitly include additional libraries for the integrations. This is now all done automatically.

Edited by Floris Westerman

Merge request reports

Loading