In the previous installment of the series, we saw that, while it is easy to convert Java to Kotlin, a lot of additional work must be done to make Kotlin Java EE compatible. It is manual work and it is error-prone, mostly due to friction between JavaBeans specification and Kotlin. JavaBeans is an old standard, literally from the last millennium. It was conceived to make component manipulation in RAD visual editors possible. For example, the user would drag and drop a text field from the toolbar to the form, and then set the text, color, and other properties. The component had to be constructed in an uninitialized state and configured step-by-step.

In a non-GUI world, this concept has many drawbacks: The component does not know when the configuration is finished, and the user does not know which properties must be set to complete the configuration.

With dependency injection (DI) frameworks, such properties would be automatically populated by the framework. A simple @Inject in front of the variable would find the correct bean. Although the solution looked elegant at the time, it is a workaround for proper object construction. It also complicates testing, as the injection must be reconfigured to use mock beans.

Luckily, the Kotlin compiler supports plugins, which can simulate proper object structure and proper object construction. Plugins can change the way compiled classes look like. They will automatize the Java EE alignment and allow some other cool tricks.

An example project before improvements can be found on GitHub. It is a result of changes from the previous part of the series.

“all-open” Compiler Plugin

Part of service class conversion is making classes and all non-public members (both methods and properties) open. Forget one method and the service will fail to initialize in the Java EE container.

We will get rid of open statements sprinkled all over the place using the "all-open" plugin. Add the plugin, and enumerate the annotations of the elements that should be open in the plugin’s configuration section. In this case, we want that for JAX-RS and data components, so add @Path and @Stateless annotations:

plugins { id "org.jetbrains.kotlin.jvm" version '1.1.1' id "org.jetbrains.kotlin.plugin.allopen" version '1.1.1' } apply plugin: 'kotlin' apply plugin: 'kotlin-allopen' ... allOpen { annotation("javax.ws.rs.Path") annotation("javax.ejb.Stateless") }





Now you can remove all open statements from the code.

“no-arg” and “jpa” Compiler Plugins

Rather than injecting the dependencies later, we will pass them to the constructor. After the constructor is called, the instance will be fully initialized and no further updates will be made; the object can be immutable from source code's point of view. This will give a nice functional feel to the class. But how to do that when beans must have a parameterless constructor?

The "no-arg" compiler plugin will add a parameterless constructor to the compiled code. There will be no visible parameterless constructor in the source code. That will force users to construct the object in an immutable way. However, frameworks will have a secret constructor of their own. It can be configured in the same way as the all-open plugin. There are also preconfigured versions for Spring and JPA, where JPA works on all classes annotated as @Entity or @Embeddable . Those additional configurations are part of the same plugin as the regular "no-arg" one, so it is not necessary to add them both, but I will for the sake of the completeness:

plugins { ... id "org.jetbrains.kotlin.plugin.jpa" version '1.1.1' id "org.jetbrains.kotlin.plugin.noarg" version '1.1.1' } ... apply plugin: 'kotlin-jpa' apply plugin: 'kotlin-noarg' ... noArg { annotation("javax.ws.rs.Path") annotation("javax.ejb.Stateless") } ...





Now we can remove all parameterless constructors, and do some interesting things like making service classes immutable. But if the class has an @Injected constructor, why does it need parameterless one, too? I was really puzzled by this requirement. Java cannot call two constructors on the same object, right? In the case of the stateless beans, it seems that one instance is constructed during initialization of the bean using parameterless constructor, and all other instances, one per call, are constructed with parametrized constructor with injected values.

Go to KittenRestService and change beginning of the class from:

@Path("kitten") class KittenRestService { @Inject private lateinit var kittenBusinessService: KittenBusinessService ...





to:

@Path("kitten") class KittenRestService @Inject constructor( private val kittenBusinessService: KittenBusinessService) { ...





Our var becames val , we got rid of lateinit , and the class is fully initialized during construction through constructor parameters.

IMPORTANT! In some cases, lateinit does not work as it should; constructor initialization seems to always work correctly. Prefer constructor parameters over lateinit .

An additional benefit is avoiding problems during bean initialization: Accessing injected fields from the parameterless constructor will cause errors because values are not yet there; that is why @PostConstruct annotation was introduced. The typical order of execution is:

Construct object with a parameterless constructor. Inject values. Call method marked as @PostConstruct .

With constructor injection, @PostConstruct is not needed anymore. Managed beans behave like any other Java class, and becomes a bit safer. As a bonus, we do not need an dependency injection framework for testing; we can simply pass dependencies to the constructor.

The parameterless constructor can be also removed from KittenEntity. Temporary default values are not needed anymore.

Jackson Kotlin Plugin

This is not a Kotlin compiler plugin, but rather a Jackson one. In addition to Kotlin type support, it provides an automatic binding between parsed data and the constructor. Setters are not needed, so the class can become immutable. As we saw in the previous article that if we want to use the constructor for binding, we must specify the JSON property name of the each parameter:

data class KittenRest( @param:JsonProperty("name") override val name: String, @param:JsonProperty("cuteness") override val cuteness: Int ) : Kitten





Before Java 8, bytecode did not contain names of the constructor parameters, so frameworks cannot bind them automatically. The Java 8 and Kotlin compilers add more metadata to the bytecode, which can be used by frameworks to do the mapping. Now declaration can be shortened to:

data class KittenRest( override val name: String, override val cuteness: Int ) : Kitten





The same can be accomplished with the special parameter of the Java compiler, but it is not enabled by default.

This plug-in must be added to two files to be enabled: build.gradle and RestApplication.kt. It requires the "kotlin-reflect" plugin to work.

build.gradle: add “jackson-datatype-kotlin” and “kotlin-reflect” as dependencies:

dependencies { compile("com.fasterxml.jackson.module:jackson-module-kotlin:$jackson_version") compile("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" ... }





RestApplication.kt: register KotlinModule:

@ApplicationPath("api") class RestApplication : Application() { private val classes = setOf(KittenRestService::class.java) private val singletons = setOf(MyContextResolver()) override fun getClasses() = classes override fun getSingletons() = singletons } class MyContextResolver : ContextResolver<ObjectMapper> { val objectMapper = ObjectMapper() init { objectMapper.registerModule(KotlinModule()) } override fun getContext(p0: Class<*>?) = objectMapper }





Note that currently, Kotlin cannot override functions with getters; if you declare classes and singletons from RestApplication as public val , the compiler will complain. You must declare properties private and provide getters with override.

In the end, the project should look similar to this.

Conclusion

Modules with their configurable rules provide “convention over coding” features, which does not mean just less typing, but also less fighting with Java EE frameworks. As a bonus, we can force JavaBeans to behave like standard Java classes, which will make them more reliable and easier to work with. We can code in a more functional style.

The project looks better, but it is still Java code in Kotlin files. The next part of the series will be less about Java EE and more about language: all classes will be converted to the idiomatic Kotlin code!