- Project LOOM (implies Java 16)
- Java 14-16 +
preview
features (thought not needed as major source langusage in Kotlin) - Gradle 6.7+
jvm-ecosystem
plugin andtoolchain
cross compilation/runtime support - Blaze-Persistence recently released compile-time
@EntityView
s feature
- Immutables in Kotlin sources!
- Mapstruct mapping to immutable DTO/Projection classes integrated with Immutables.
- Arrow-kt (lesser in recent versions, just annotation processor, will be fully fledged in future works)
Some planned key features
- GraphQL support for all platform (e.g. with JPA projections)
The goal of this (JVM ecosystem) POC is to run the same production-ready source code in multiple runtimes without suffering from any portability issues. This will help to understand which "best practices" in writing common Spring-Boot-ish microservice application are really portable and at the same time are not an overkill. As a simple example - using one annotaations instead another may where one may be used by more frameworks and runtimes, or the opposite - avoiding vendor-locking by removing most/all vendor specific imports, etc. The current repository source-tree already uses quite a bleeding edge setup for JVM ecosystem.
#Micronaut
./gradlew run
#Quarkus
./gradlew quarkusDev #(you need to execute this command twice! Ctrl+C after first startup failure, and execute again)
#Spring
./gradlew bootRun
- Quarkus strictly requires
beans.xml
in every subproject/module. - Quarkus in some cases requires a subproject/module to have an
src/main/java
folder (even if empty) otherwise the build fails.
Almost all issues are as consequence of multi-module project setup and Kotlin - which both very desirable!
-
Tests do not run. As simple as it sounds - with kotlin setup tests are not detected at all. at least it was so for
0.15.0
for sure and for0.16.0
(almost) for sure, even though:quarkus { setSourceDir("src/main/kotlin") resourcesDir() += file("src/main/resources") setOutputDirectory("$buildDir/classes/kotlin/main") }
you get
org.junit.jupiter.api.extension.TestInstantiationException : TestInstanceFactory [io.quarkus.test.junit.QuarkusTestExtension] failed to instantiate test class [com.example.marvel.web.rest.jaxrs.EmployeeServiceOperationsNamespaceTest] : The test class class com.example.marvel.web.rest.jaxrs.EmployeeServiceOperationsNamespaceTest is not located in any of the directories [classes/java/test, /test-classes, bin/test]
There is a Test com.example.marvel.web.rest.jaxrs.EmployeeServiceOperationsNamespaceTest You can uncomment and try.
-
Multi-module project with with Quarkus is annoying. Recompilation never works here. Always an error. But also, simple restart didn't work either due to quarkusio/quarkus#2413 (it didn't work in 100%!! of cases, and I then simplified Gradle build logic - it begin to sometime restart fine). By restarting fine I mean many times slower than some Spring-Boot application because Quarkus obviously does a lot of things at build time - which is intended for to allow hot-reload, but when the later do not work - it slows things down drastically. The more complex Gradle setup the more chances that simple restart (say by telling IDEA to restart the app) won't work. A single reproducer for the quarkusio/quarkus#2413 is
./gradlew runtime-jakarta:quarkusDev
-> change sources ->curl
to let Quarkus start to recompile -> you'll get "recompilation failed" -> now if you restart the app from IDEA you got quarkusio/quarkus#2413. -
Changing Javadoc/Kdoc cause Quarkus to recompile on request as if that were method bodies, etc. at least with Kotlin (Could be this one is unresolvable, don't know - it is not big issue if recompilation would work great).
-
See all
FIXME:
andTODO:
the most annoying one is quarkusio/quarkus#2196. Even thoughbeans.xml
in place and even writing jandex index with gradle plugin - nothing works - still getting "This method is normally overridden". (resulting message like in quarkusio/quarkus#1717) -
For some reason both
@kotlin.jvm.Transient
and@javax.persistence.Transient
results in absence of the property in DB table for RDMS. Not sure it this intended and on which side (e.g.persistence
spec, kotlin or quarkus...) -
There was an exception saying
com.example.marvel.domain.record.RecordId
"is not found/indexed" or "should be declared as entity" - so we annotated it with@javax.persistence.Embeddable
- which is not compliant with Hibernate (at least for@javax.persistence.IdClass
which it is, not sure about compliance with JPA) and it worked. Would be great to avoid it. -
Kotlin. Kotlin interop is very non-complete. Having
PanacheRepository*
as interfaces with default methods is great solution,PanacheEntity
is understandablyabstract class
as it have a field, but what is the reason to leavePanacheEntityBase
an abstract class. This limits developer experience as you can't have other logic accumulated elsewhere in other class as there could be only oneclass
you extend from. ShouldPanacheEntityBase
become an interface? -
Kotlin. If you make
@javax.persistence.Id
property aval
Hibernate throws it can not set id for final field. In Kotlin this is common and fordata classes
with Spring-Boot( Data) you normally make itval
even though in Java you probably would have a setter. Enhance at build time instead limiting developer experience with Kotlin? -
JWT. Making Endpoint
RequestScopped
for to allow MPJwtToken
injection seems limiting. There always was request scoped things you can inject in JAX-RS runtime (with@Context
annotation) and it worked for years. Maybe for MPJwtToken
and companions related to same concerns there could be possibility to@Inject
into@ApplicationScopped
still? -
Kotlin. Extending
PanacheEntity*
gives much less as you can't accessstatic
method. All static methods inPanache(Repository|Entity)*
needs more design with Kotlin in mind. Default methods seems to have less problems here. The only one comes in mind is that you can't override them unless annotate method with@JvmDefault
but this is acceptable trade-off. Statics much less consumable from Kotlin. The best would be to write the extendable part of framework (e.g.quarkus-hibernate-orm-panache
) in kotlin with full compatibility with Java. OkHttp recently did it. They rewrote the wrole thing in Kotlin while maintaining full interop with Java. I personally bumped the version to4.0.0-rc-1
and it worked with no problem. See example in https://github.com/square/okhttp/blob/master/okhttp/src/main/java/okhttp3/RequestBody.kt. For 6-8 target classes inio.quarkus.hibernate.orm.panache.*
package seems like a viable solution? -
Kotlin. Recently (in
0.16.0
I think) addedcontext-propagation
does not work for coroutines. This caused to throw away allcoroutines
supporting libs (org.litote.kmongo:kmongo-coroutine
,io.vertx:vertx-lang-kotlin-coroutines
, and especiallyarrow-kt
as all those functional compositions only makes sense for async and parallel computations which could be expressed more concisely in imperative style (arrow-kt
bindings), but they all rely on coroutines in its core. Hope this will change withpanache-rx-coroutines
even though list of supported JPA annotations/features is not impressive / big at all. -
When add
io.quarkus:quarkus-reactive-pg-client
you gotio.reactiverse:reactive-pg-client
transitively.io.reactiverse:reactive-pg-client
have kotlin package with extensions, which is misleading and Quarkus user should only usesmallrye-*-client
APIs for operations, I suppose. -
While Jsonb writes our specifically structured
data class
es (see com.example.marvel.domain.model.api.record.RecordDto) just fine at runtime only inspectingpublic
properties (which IS desired), produced OpenAPI spec file apparently inspects model through reflection with Jackson probably. Either way, the resulting JSON model examples reflectsdelegate
property. Should there be whether way to provide configuration to underling Reader or something like that? Staring from2.0.7
swagger-core
provides both Gradle and Maven plugins, which both works brilliantly and has all handles I mentioned (for Jackson, for Readers, Filters, etc. AND for files to merge and affect the resulingopenapi.yaml
). So, far it is the best option on the market. You can see api.yaml with security declaration and meta-info and produced openapi.yaml file. You can do whatever to your endpoints and then just execute./gradlew resolve
in this project and rich openapi file will be written at build time, with no runtime-dependencies at all. Micronaut supports same kind of merging additional info as io.swagger.v3.plugins.gradle.tasks.ResolveTask.openApiFile
-
Micronaut and Quarkus both doesn't go well with default time arguments and inheritance by delegation we got
Caused by: javax.enterprise.inject.AmbiguousResolutionException: Ambiguous dependencies for type com.example.marvel.service.EmployeeOperationsServiceNamespace and qualifiers [@Default] - java member: com.example.marvel.web.rest.jaxrs.EmployeeOrchestrationResource#<init>() - declared on CLASS bean [types=[com.example.marvel.web.rest.jaxrs.EmployeeOrchestrationResource, com.example.marvel.service.EmployeeOperationsServiceNamespace, kotlin.coroutines.CoroutineContext, java.lang.Object, com.example.marvel.web.rest.EmployeeResourceAdapter], qualifiers=[@Default, @Any], target=com.example.marvel.web.rest.jaxrs.EmployeeOrchestrationResource] - available beans: - CLASS bean [types=[com.example.marvel.web.rest.jaxrs.EmployeeOrchestrationResource, com.example.marvel.service.EmployeeOperationsServiceNamespace, kotlin.coroutines.CoroutineContext, java.lang.Object, com.example.marvel.web.rest.EmployeeResourceAdapter], qualifiers=[@Default, @Any], target=com.example.marvel.web.rest.jaxrs.EmployeeOrchestrationResource] - CLASS bean [types=[com.example.marvel.web.rest.jaxrs.EmployeeBlockingServiceNamespaceImpl, com.example.marvel.service.EmployeeOperationsServiceNamespace, io.quarkus.hibernate.orm.panache.PanacheRepositoryBase<com.example.marvel.domain.employee.EmployeeEntity, java.lang.Long>, java.lang.Object], qualifiers=[@Default, @Any], target=com.example.marvel.web.rest.jaxrs.EmployeeBlockingServiceNamespaceImpl] at io.quarkus.arc.processor.Beans.resolveInjectionPoint(Beans.java:393) at io.quarkus.arc.processor.BeanInfo.init(BeanInfo.java:366) at io.quarkus.arc.processor.BeanDeployment.init(BeanDeployment.java:286) ... 14 more
-
If kapt fails with weird conditions:
./gradlew clean kaptKotlin --rerun-tasks
-
All those
operator
overloading insealed class
es doesn't serve much value at the moment and more a design experiment.