1. Overview
Java and Kotlin are both languages designed for the JVM. However, if we were to try and use Kotlin in Java EE container, we’d immediately run into some idiomatic challenges.
In this tutorial, we’ll take a look at those challenges and how to address them effectively.
2. Challenges
Java and Kotlin are somewhat different languages. The syntax is different, that is obvious, but that is not the real issue. It’s language design and paradigm differ, and that creates a few problems for usage within Java EE containers. To build enterprise applications with Kotlin, we need to meet these differences.
For example, Kotlin classes are final by default. To make them extensible, we need to explicitly open them. We also need to provide parameterless constructors for usage with frameworks such as JPA or Jackson. In Java, they’re available by default, but in Kotlin we need to do some extra work. The injection which containers use heavily will also be a bit tricky because of initialization, as will the integration testing with Arquillian.
Let’s address these challenges. Our example will demonstrate how to build a simple CRUD application in Kotlin and run it in a Java EE container. We’ll start off with a simple data class and then we’ll head on to write services and integration tests.
3. Dependencies
So, first things first, let’s add the javaee-api dependency:
<dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifactId> <version>8.0.1</version> <scope>provided</scope> </dependency>
Note that we’re setting this dependency as optional since we only need it for compilation.
4. JPA Entity
Now, let’s write a simple Student data class which we’ll use as an entity and as a DTO at the same time.
We’ll need the student’s id, first and last name:
@Entity data class Student constructor ( @SequenceGenerator(name = "student_id_seq", sequenceName = "student_id_seq", allocationSize = 1) @GeneratedValue(generator = "student_id_seq", strategy = GenerationType.SEQUENCE) @Id var id: Long?, var firstName: String, var lastName: String ) { constructor() : this(null, "", "") constructor(firstName: String, lastName: String) : this(null, firstName, lastName) }
We’ve defined Student as a data class, which is a special kind of class in Kotlin for holding data. In these classes, the compiler automatically derives common functions such as equals(), hashCode(), and toString() from all properties declared in the primary constructor.
Automatic generation of functions makes data classes very convenient and easy to use. However, data classes also have to fulfill some rules. For example, they have to have a primary constructor with at least one parameter marked as val or var.
In our data class, we defined all members inside the primary constructor.
We have also defined two secondary constructors:
- First, we have a parameterless constructor which container and common frameworks such as JPA or Jackson require in order to instantiate the class and populate the data with setters.
- Second, we have a convenience constructor which we use when we want to instantiate a new object without an id, which we typically use when we want to save a new entity to the database.
Additionally, we use standard JPA annotations to define the JPA entity just as we do in standard Java EE.
3. Business Service
We need a business service to handle the CRUD operations with an EntityManager. It’s simple and very similar to Java implementation, with a few notable differences:
@Stateless open class StudentService { @PersistenceContext private lateinit var entityManager: EntityManager open fun create(student: Student) = entityManager.persist(student) open fun read(id: Long): Student? = entityManager.find(Student::class.java, id) open fun update(student: Student) = entityManager.merge(student) open fun delete(id: Long) = entityManager.remove(read(id)) }
Since all classes in Kotlin are final, we need to explicitly open our class to enable the extension. Java EE container needs the extension because it’ll create a proxy from our service class and inject it where it’s required. Public methods also have to be open otherwise the proxy cannot be created. Therefore, we use the open keyword on the class and all its public methods.
EntityManager is used in a similar way as in Java with the @PersistenceContext annotation. We define it as a private member with an additional lateinit keyword. This keyword tells the compiler that this variable is null at first, but that it’ll be initialized before the first use. This eliminates unnecessary null checks and aligns perfectly with container injection. We’ll use it every time we use the @Inject annotation.
4. REST Service
Finally, we need to define a REST service endpoint for our application. To do that, we need to register our resource class:
@ApplicationPath("/") class ApplicationConfig : Application() { override fun getClasses() = setOf(StudentResource::class.java) }
Now, we define our resource class. It’ll be very close to what we’d do in Java, with a few Kotlin-specific modifications:
@Path("/student") open class StudentResource { @Inject private lateinit var service: StudentService @POST open fun create(student: Student): Response { service.create(student) return Response.ok().build() } @GET @Path("/{id}") open fun read(@PathParam("id") id: Long): Response { val student = service.read(id) return Response.ok(student, MediaType.APPLICATION_JSON_TYPE).build() } @PUT @Path("/{id}") open fun update(@PathParam("id") id: Long, student: Student): Response { service.update(student) return Response.ok(student, MediaType.APPLICATION_JSON_TYPE).build() } @DELETE @Path("/{id}") open fun delete(@PathParam("id") id: Long): Response { service.delete(id) return Response.noContent().build() } }
We explicitly opened our resource class and all the public methods, like in the previous example with the business service. We also used the lateinit keyword again, this time for the injection of our business service into the resource class.
5. Testing with Arquillian
We’ll now implement the integration testing of our application with Arquillian and Shrinkwrap.
To test our application with Arquillian, we need to set up the packaging and the deployment to the test container with ShrinkWrap:
@RunWith(Arquillian.class) public class StudentResourceIntegrationTest { @Deployment public static WebArchive createDeployment() { JavaArchive[] kotlinRuntime = Maven.configureResolver() .workOffline() .withMavenCentralRepo(true) .withClassPathResolution(true) .loadPomFromFile("pom.xml") .resolve("org.jetbrains.kotlin:kotlin-stdlib") .withTransitivity() .as(JavaArchive.class); return ShrinkWrap.create(WebArchive.class, "kotlin.war") .addPackages(true, Filters.exclude(".*Test*"), "com.baeldung.jeekotlin") .addAsLibraries(kotlinRuntime) .addAsResource("META-INF/persistence.xml") .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml"); } // more code }
Deployment is similar to typical Java deployments for the integration testing, but it contains some additional configuration. Here we use the Shrinkwrap Maven Resolver to retrieve the kotlin-stdlib from the Maven repository. We then archive it to our WAR as a library.
After that, we use the HttpClient to run CRUD requests against our REST API:
@Test @RunAsClient public void when_post__then_return_ok(@ArquillianResource URL url) throws URISyntaxException, JsonProcessingException { String student = new ObjectMapper() .writeValueAsString(new Student("firstName", "lastName")); WebTarget webTarget = ClientBuilder.newClient().target(url.toURI()); Response response = webTarget .path("/student") .request(MediaType.APPLICATION_JSON) .post(Entity.json(student)); assertEquals(200, response.getStatus()); }
In this example, we use @ArquillianResource to provide the URL for the API, then we serialize the Student object and POST it to the API. If everything is okay and the object has been created in the database, the response status is 200 OK, which we assert at the end of the test.
6. Conclusion
In this article, we demonstrated how to build CRUD REST JPA application in Kotlin, how to deploy it, how to run it in the Java EE container and how to test it with Arquillian. As you can see, Kotlin and Java work well together but we have to do some additional work to make it happen.
The complete source code for the example is available over on GitHub.