- Project LOOM (implies Java 16)
- Java 14-16 +
previewfeatures (thought not needed as major source langusage in Kotlin) - Gradle 6.7+
jvm-ecosystemplugin andtoolchaincross compilation/runtime support - Blaze-Persistence recently released compile-time
@EntityViews 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.xmlin every subproject/module. - Quarkus in some cases requires a subproject/module to have an
src/main/javafolder (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.0for 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 ->curlto 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.xmlin 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.Transientand@javax.persistence.Transientresults in absence of the property in DB table for RDMS. Not sure it this intended and on which side (e.g.persistencespec, 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.IdClasswhich 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,PanacheEntityis understandablyabstract classas it have a field, but what is the reason to leavePanacheEntityBasean abstract class. This limits developer experience as you can't have other logic accumulated elsewhere in other class as there could be only oneclassyou extend from. ShouldPanacheEntityBasebecome an interface? -
Kotlin. If you make
@javax.persistence.Idproperty avalHibernate throws it can not set id for final field. In Kotlin this is common and fordata classeswith Spring-Boot( Data) you normally make itvaleven though in Java you probably would have a setter. Enhance at build time instead limiting developer experience with Kotlin? -
JWT. Making Endpoint
RequestScoppedfor to allow MPJwtTokeninjection seems limiting. There always was request scoped things you can inject in JAX-RS runtime (with@Contextannotation) and it worked for years. Maybe for MPJwtTokenand companions related to same concerns there could be possibility to@Injectinto@ApplicationScoppedstill? -
Kotlin. Extending
PanacheEntity*gives much less as you can't accessstaticmethod. 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@JvmDefaultbut 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-1and 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.0I think) addedcontext-propagationdoes not work for coroutines. This caused to throw away allcoroutinessupporting libs (org.litote.kmongo:kmongo-coroutine,io.vertx:vertx-lang-kotlin-coroutines, and especiallyarrow-ktas all those functional compositions only makes sense for async and parallel computations which could be expressed more concisely in imperative style (arrow-ktbindings), but they all rely on coroutines in its core. Hope this will change withpanache-rx-coroutineseven though list of supported JPA annotations/features is not impressive / big at all. -
When add
io.quarkus:quarkus-reactive-pg-clientyou gotio.reactiverse:reactive-pg-clienttransitively.io.reactiverse:reactive-pg-clienthave kotlin package with extensions, which is misleading and Quarkus user should only usesmallrye-*-clientAPIs for operations, I suppose. -
While Jsonb writes our specifically structured
data classes (see com.example.marvel.domain.model.api.record.RecordDto) just fine at runtime only inspectingpublicproperties (which IS desired), produced OpenAPI spec file apparently inspects model through reflection with Jackson probably. Either way, the resulting JSON model examples reflectsdelegateproperty. Should there be whether way to provide configuration to underling Reader or something like that? Staring from2.0.7swagger-coreprovides 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 resolvein 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
operatoroverloading insealed classes doesn't serve much value at the moment and more a design experiment.