Thursday, May 18, 2023

Packaging and Running a Spring Boot Application with Kotlin - Microservice Cloud Native

 Packaging and running a Spring Boot application 

Now that we know something more about building microservices within Spring Boot, we need to package and run our service. This will teach us how to package a Spring Boot application. Then, we will analyze the difference between packaging, using JARs or WARs. Finally, we learn how to run the package applications, and how to creating self-executables JARs.

Packaging We can package the application using the Maven lifecycle phase package, either using the IntelliJ Maven projects window or from the command line. We can open a command line to the project folder from IntelliJ using the terminal window from the menu—View | Tool Windows | Terminal or using the Alt + F12 keybinding. mvnw package Running this from our example project will create a file named chapter2-0.0.1-SNAPSHOT.jar under the target folder. This command will create a JAR that contains all dependencies required by the application that we are packaging, in our case all the Spring components and third-party libraries that our microservice uses, this is commonly called a Fatjar and allows us to distribute or archive our microservice into a single binary.

Making JARs not WARs 

Spring Initializr builds JARs by default, but we can change it to produce WARs instead, if we need to deploy the application in an application server, for example, an external Tomcat server, however, we don't recommend it. If we go back to our microservices' principles, we should remember that we need to provide a loosely coupled microservice, so if we deploy it in an application server we are coupling our application to it. If tomorrow that server changes, it will affect us. Spring Boot provides a fully working Tomcat server that starts in seconds and is fully configured for our needs. This allows that everything that is required for our application is under the application control. We need to remember to keep updating the version of Spring Boot that we use. The Spring team is constantly doing releases, allowing us to keep up to date with the latest Tomcat versions.

This will benefit our infrastructure as well, as our microservices can run in any box that just has Java JRE on it, no other software or system is required. This makes them a perfect candidate for Cloud-Native microservices that could spawn quickly in any created box or container.

Running Spring Boot applications 

Now that we have our microservices packaged, we need to run them, for example from our command line. To run our example, we can just enter the following: java -jar target/chapter2-0.0.1-SNAPSHOT.jar This will launch the application and finally show something like the following: Tomcat started on port(s): 8080 (http)

Started Application.Kt in 2.274 seconds (JVM running for 2.619) We can press Ctrl + C to stop it at any time. This is exactly the same when we execute the goal run on the Spring Boot Maven plugin, but we are not using Maven, we don't need Maven for running our JARs, neither a JDK, just a JRE (Java Runtime Environment).

Creating executable JARs 

In a UNIX system, when we need to run a JAR as a process, either init.d or systemd, we can't just run the JAR itself, we may need to create a shell script to launch our application calling the Java command line, too. Spring, if configured to do so, will create a special script at the beginning of the JAR file that will allow it to be used as an executable. We need to modify our POM to add this feature, under the build/plugins tags: <build>

....

 <plugins>

  <plugin>

   <groupId>org.springframework.boot</groupId>

   <artifactId>spring-boot-maven-plugin</artifactId>

   <configuration>

    <executable>true</executable>

   </configuration>

  </plugin>

....

</build>

Now we can directly invoke our JAR as a script, for example from the command line: ./target/chapter2-0.0.1-SNAPSHOT.jar This will execute our JAR directly. This allows any UNIX system to execute that script when the JAR is invoked. This is a feature that we may take advantage of to simplify how we start microservices in UNIX systems.

Configuring our application When we create a Spring Boot application, we may need to use configuration values, things that we want to externalize from the application code itself, or that we may want to change without affecting the main code. First, we will make some changes in our service and review the different methods to configure the application. Then, we will understand what Spring Expression Language is, and how we can use application profiles. Finally, we will apply our knowledge of configuration to create some conditional beans.

Setting configuration values 

There is a time in any application that we may need to have a configurable value, something that we can define outside our main code and that can easily change, let's get an understanding through an example, modifying our service class: 

package com.microservices

 import org.springframework.beans.factory.annotation.Value

import org.springframework.stereotype.Service

 @Service

class ExampleService : ServiceInterface {

  @Value(value = "\${service.message.text}")

  private lateinit var text: String

 override fun getHello(name : String) = "$text $name"

In this change, we have introduced a new variable that has a configurable value using the @Value annotation.

This will be filled with the configuration value provided by the expression ${service.message} this is a Spring Expression Language query, later on in this section, we will understand that concept further. You may notice that we have escaped the $ character using \$, this is because the Kotlin compiler will understand a $ in a string as an inline string, but actually here it is part of a literal string used by Spring. If we try to run our microservice now, it will not start and will get an error instead: Could not resolve placeholder 'service.message.text' in value "${service.message.text}" This is because we have defined a configuration value but we haven't provided any configuration to the application. There are several ways to configure a Spring application, but we will review the three that we are going to use to develop microservices: Properties Yaml Command line arguments

Using properties We can define configuration values using the properties file. Spring Initializr has already created an application.properties in the resources folder of our project, we can edit it to add this: SERVICE.MESSAGE.TEXT="hello" You may notice that the label of this properties file is in uppercase but the value is referenced in lower case, this is because when we query the configuration we ignore case, and in properties files, it is more standard to have labels in capital letters. Then, if we run our application it will work as expected, however, the properties file is just a plain text file that may look too complex for multi-level configurations.

Using Yaml 

First, we will rename our application.properties into application.yml, then we can change it to the following: service:

  message:

    text: "hello" This looks better for more complex configurations and will be our preferred method in the rest of the blog. In this example, the labels in the yaml file are in lowercase. This is because, even when able to ignore case as before, it is more standard in yaml files to be either in lowercase or camel case.

No comments: