Skip to Content
Technical Articles

How to create a Kotlin project and deploy the RESTful web service successfully on SAP Cloud Platform Neo

In this post, you will learn how to develop and deploy a simple Kotlin RESTful web service with two endpoints to Neo and where you need to pay attention to get it running without errors.

The first step is to create a Kotlin project. Ideally, this can be done via https://start.spring.io with the specifications of using Maven as Project Type, using Kotlin and adding web dependencies. Important dependencies will be added later.

The designated project does not use any database as these procedures can be found easily on the web. This project will only contain a simple class with a controller and a class using static variables to temporarily save data.

As there is no direct static class/methods existing in Kotlin, the “object” type is been used.

Firstly, an object called PlayersObject will be created:

object PlayersObject {
	var player1 : String = ""
	var player2 : String = ""
}

The “var” declaration allows the attributes of the object to be changed. Getters and setters are not necessary as Kotlin creates them during runtime. Other than it is familiar from Spring Boot, getter and setter methods are not been called by get() or set(T t) but changed by directly accessing the attribute via dot notation (item.property = “abc”).

When this Kotlin object has been created, it is time to add a controller. The controller will govern any REST endpoints and will register them and listen to them.

@RestController
@RequestMapping("/test")
class PlayerController{

	@PostMapping("/addPlayer")
	@ResponseStatus(HttpStatus.OK)
	fun initPlayer1(@RequestBody players : InitPlayerBody){
		PlayersObject.player1 = players.firstPlayer
		PlayersObject.player2 = players.secondPlayer
	}
	
	@GetMapping("/getPlayer")
	fun getPlayer() = PlayersObject

}

This controller registers two endpoints (addPlayer1 and player1) behind the /test path. Because we want to require a standard post body format for “addPlayer1”, we use a data class object as input parameter.

data class InitPlayerBody(
	var firstPlayer : String,
	var secondPlayer : String){
}

The POST endpoint now requires a JSON object with the attributes firstPlayer and secondPlayer. Any wrong JSON body will cause a bad request. The GET endpoint from the controller will return the attributes of PlayersObject JSON-encoded with this simple syntax. Compared to Java using Spring Boot, a lot of LoC and files could be saved using Kotlin and Spring Boot.

The next step is to modify the Application class. This is important since Neo wants the compiled project in WAR format, so there are adjustments to be done, so we avoid to get at all endpoints later 404 – Not found. This issue occurs because Tomcat is not been initialized probably in WAR package and no servlet is been created. Add to the class name following syntax: :SpringBootServletInitializer()

The results should look like this:

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer

@SpringBootApplication
class Application : SpringBootServletInitializer()

fun main(args: Array<String>) {
	runApplication<Application>(*args)
}

After this has been completed, there are a variety of dependencies needed for this project. Mainly, to run correctly and avoid Neo logging issues.

Most importantly, a packaging tag shall be added to the pom, defining the target as WAR file rather JAR file.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.6.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<packaging>war</packaging>
	...

	<properties>
		<java.version>1.8</java.version>
		<kotlin.version>1.2.71</kotlin.version>
	</properties>

	<dependencies>

		<dependency>
			<groupId>com.sap.cloud</groupId>
			<artifactId>neo-java-web-sdk</artifactId>
			<version>3.83.3</version>
			<scope>provided</scope>
			<type>zip</type>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
			<exclusions>
				<exclusion>
					<groupId>org.springframework.boot</groupId>
					<artifactId>spring-boot-starter-tomcat</artifactId>
				</exclusion>
				<exclusion>
					<groupId>org.apache.tomcat.embed</groupId>
					<artifactId>tomcat-embed-el</artifactId>
				</exclusion>
				<!-- Exclude logback and SpringBoot logging, otherwise the app won't start on SAP Cloud Platform -->
				<exclusion>
					<artifactId>logback-classic</artifactId>
					<groupId>ch.qos.logback</groupId>
				</exclusion>
				<exclusion>
					<groupId>org.springframework.boot</groupId>
					<artifactId>spring-boot-starter-logging</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-tomcat</artifactId>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>com.fasterxml.jackson.module</groupId>
			<artifactId>jackson-module-kotlin</artifactId>
		</dependency>
		<dependency>
			<groupId>org.jetbrains.kotlin</groupId>
			<artifactId>kotlin-reflect</artifactId>
		</dependency>
		<dependency>
			<groupId>org.jetbrains.kotlin</groupId>
			<artifactId>kotlin-stdlib-jdk8</artifactId>
		</dependency>


		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>com.google.code.gson</groupId>
			<artifactId>gson</artifactId>
			<version>2.8.5</version>
		</dependency>

		<!-- Neo specific ones -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-core</artifactId>
			<scope>provided</scope>
		</dependency>

		<dependency>
			<groupId>org.apache.httpcomponents</groupId>
			<artifactId>httpclient</artifactId>
			<version>4.5.5</version>
		</dependency>

	</dependencies>

	<build>
		<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
		<testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
			<plugin>
				<groupId>org.jetbrains.kotlin</groupId>
				<artifactId>kotlin-maven-plugin</artifactId>
				<configuration>
					<args>
						<arg>-Xjsr305=strict</arg>
					</args>
					<compilerPlugins>
						<plugin>spring</plugin>
					</compilerPlugins>
				</configuration>
				<dependencies>
					<dependency>
						<groupId>org.jetbrains.kotlin</groupId>
						<artifactId>kotlin-maven-allopen</artifactId>
						<version>${kotlin.version}</version>
					</dependency>
				</dependencies>
			</plugin>
		</plugins>
	</build>

</project>

Most important dependencies are kotlin-dependencies from JetBrains and to define slf4j and logback as provided, otherwise they will not allow the Java application to start on Neo. Furthermore, neo-java-web-sdk is necessary to run later on Neo. These steps are mandatory not only for Kotlin but also for any standard Java or Spring Boot application.

When all of the previous steps have been completed, your project is ready for a build and deployment. With “mvn clean build” you create a target WAR package including your compiled Java project as well as dependencies included.

When the target WAR file has been successfully created, it’s ready to be deployed to Neo. With Postman you can fire a request to the POST endpoint (/test/addPlayer) and store like this data.

Sample payload:

{
	"firstPlayer": "John Doe",
	"secondPlayer": "Joseph Test"
}

The designed endpoint will now process this input and save the values into the static attributes player1 and player2. Calling the GET endpoint (/test/getPlayer) will return a JSON like:

{
	"player1": "John Doe",
	"player2": "Joseph Test"
}

And voilĂ , a simple Kotlin Spring Boot RESTful Web Service has been developed and deployed to SAP Cloud Platform Neo. I hope you had no problems, and everything works fine. If yes, please leave a like, and if not, write into the comment section below. Either way, good luck working with Kotlin and Spring Boot.

Be the first to leave a comment
You must be Logged on to comment or reply to a post.