Annotation-based definition of routes in Ktor

As you might know or have seen in my previous blog, the declaration of the routes and HTTP methods is done programmatically within Kotlin Ktor.

If you are used to specifying this information through annotation in Jakarta EE, it might feel a bit awkward. Although, due to the ‘trailing lambdas’ construct, this almost looks and feels like an annotation.

But actually, you can have something similar to annotations from the Jakarta EE world, with a little custom code.

Standard Ktor way

As mentioned, the trailing lambdas solutions allow you to define the routes in a very similar way to Jakarta EE. For each HTTP method, there exists a method with the same name that accepts 2 parameters. The part of the URL and a lambda which is executed when there is a request that matches the URL defined in the first parameter.

In this snippet, there are a few examples

    get("/hello/{name?}") {
        val name = call.parameters["name"] ?: return@get call.respond(
                "Missing name parameter"
        val language = call.request.queryParameters["language"] ?: "en"
        call.respondText("Hello $name with language $language")

    post("/person") {
        val person = call.receive<Person>()
        call.respondText("POST received with name ${} and age ${person.age}")

The required values from the request like path and query parameters or the body of the request can be retrieved from the call object passed into the lambda. This same object is also used to return the result to the client.

And this declaration, although programmatically and with no reflection or scanning required, is similar to the way we would define those methods in Jakarta EE.

public String sayHello(@PathParam("name") String name, @QueryParam("language") String language) {


Annotations solution

As mentioned in the introduction, with a little help of custom code, you can have a solution that uses annotations on functions and is very similar to the Jakarta EE approach.

Let us first look at an example

suspend fun greeting(call: ApplicationCall, name: String, @DefaultValue("en") language: String?) {
    // function parameter names must match either variable name in URL pattern or is matched against query parameters
    call.respondText("Hello $name with language $language")

You can see that we define a method that has a GET annotation. The value of the annotation is a reference to a variable name that we also have as the function parameter name. But we can also retrieve query parameters by just defining them as parameters.

When we define a parameter as optional like in the example, the query parameter language is not required in the URL. If we do not specify it as optional and the user calls the endpoint without specifying the query parameter, you get an exception.

In case the parameter is optional, we can define a default value by using the custom annotation @DefaultValue as we see in the example.

Besides the possibility to catch path and query parameters as function parameters, we can also capture the body of the request. An example can be seen in the next snippet

suspend fun savePerson(call: ApplicationCall, person: Person) {
    // The body can be retrieved by defining a function parameter. Conversion from JSON happens automatically.
    call.respondText("POST received with name ${} and age ${person.age}")


Here we capture the body of the Post request and convert the JSON to a Person instance.

In all cases, we also pass the ApplicationCall instance to the method so that we can perform more logic based on request values and send the response back to the client.

The last thing we need to do is indicate where the methods can be found that have those annotations. Since Ktor doesn’t make use of a scanning mechanism, we need to provide the classes with those annotated functions.

The custom code wraps these functions in the regular Route definition of Ktor. This statement using the custom function processRoutes processes the functions that have those custom annotations and makes it transparent for Ktor.

routing {
    processRoutes(HelloResource(), JsonResource())


The code is based on a gist from Thomas van den Bulk.


If you like to use the annotation-based configuration of endpoints in a similar way as Jakarta EE, you can use the code that is showcased in the project Ktor-annotated available within this GitHub repository.

A very small set of functions allow you to define annotations on Kotlin functions that define the Http method and call the function when a matching request comes in converting path and query parameters to the function parameters.

It can help those familiar with the way of defining REST endpoints within Jakarta EE but also simplifies handling path and query parameters for those familiar with Ktor.

Training and Support

Do you need a specific training session on Kotlin, Jakarta EE or MicroProfile? Or do you need some help in getting up and running with your next Ktor project? Have a look at the training support that I provide on the page and contact me for more information.

JAX-RS Kotlin Ktor

Why don’t you create your next Backend application with Kotlin and Ktor?

For a long time, Kotlin was on my list of things I wanted to learn more about. In the second half of 2022, I finally got the chance to learn the language and started using Ktor, the ‘web application runtime’ for Kotlin.


Kotlin is a modern programming language that offers several benefits over Java. Firstly, Kotlin supports object-oriented, functional, and procedural programming constructs, making it a versatile language for a wide range of projects.
Secondly, Kotlin is fully interoperable with Java, which means that developers can easily use Kotlin in conjunction with existing Java codebases. Additionally, Kotlin offers support for several concepts, such as data classes, structured concurrency, and virtual threads, which were not available in Java until much later. Kotlin also offers null safety features, which help prevent common errors that can arise due to null pointer exceptions.
Another significant benefit of Kotlin is its compact and highly readable grammar, which makes code more concise and easier to understand. Finally, Kotlin offers extension functions, which allow developers to add new functions to existing classes, making it easier to write reusable code.

Kotlin is a modern, powerful, and highly expressive programming language that helps developers write better code faster and more efficiently.

We will encounter various powerful constructs of Kotlin in this blog series called “project FF”. In this Framework Face-off, I will compare several Java application runtimes, including Spring Boot and Quarkus, with Jakarta EE and how you implement Enterprise functionality.

But today, I give already two examples of the power of Kotlin.

Kotlin extension functions

Kotlin extension functions allow you to add new functions to an existing class without having to inherit from that class or modify its source code.
In other words, extension functions let you extend the functionality of a class in a more flexible and modular way. This can make your code more readable and easier to maintain.

Extension functions are similar to static utility methods, but with the added benefit of being able to call them as if they were instance methods of the class, they are extending. This can make your code more concise and easier to understand.

fun String?.hasSpaces(): Boolean {
   val found = this?.find { it == ' '}
   return found != null

fun main() {
   val data = "A sentence with spaces in of course"
   println(data.hasSpaces()) // True

   val data2 = "word"
   println(data2.hasSpaces()) // False

For a detailed explanation of this code, I refer you to the Kotlin tutorials. In short, the hasSpaces function is defined on a nullable String type. The function body checks if we find a space in the string value. From that point on, we can use the hasSpaces on a String as you can see in the main function.

Creating an interceptor

You can create an interceptor in plain Kotlin using lambdas and high-order functions. Functions and lambdas are first-class citizens and are treated equally as data objects.

inline fun timeBlock(block: () -> T): Pair {
   val startTime = System.nanoTime()
   val result = block()               // execute lambda
   val endTime = System.nanoTime()
   return Pair(result, endTime - startTime)

fun main() {
   val number = 121
   val (isPrimeValue, time) = timeBlock {
      (2..number/2).none { number % it == 0 }

   println("Result of prime check for $number is $isPrimeValue ")
   println("Execution took $time nanoSeconds ")

Here we define a function that accepts a lambda and we time the execution of the lambda. The function returns the result of the lambda and the time it took.

As example, we time the calculation to check if a value is a prime value. Also here you can see the power of Kotlin but still keep it readable.

(2..number/2).none { number % it == 0 }

defines that we go from value 2 until the half of the value we need to check and there should be no value where the modulo of the value with the number is 0.


Ktor is a lightweight, asynchronous web server that easily allows you to define what is needed. In the blog “Time is code, My friend”, Ktor performed really well and performed as good as Quarkus which has a very elaborate and complex pre-processing system to achieve such a fast startup.

Since it is highly modular, you can keep the runtime, and memory usage very low. And since there are modules available for each major topic or framework, you can create applications that integrate with them very easily.

Also, Ktor is created with Kotlin Coroutines support built-in. Kotlin Coroutines provide you with solutions for asynchronous non-blocking programming, structured concurrency and solutions similar to Java Virtual Threads. Things that will only appear in Java in the coming versions but are already available for many years within Kotlin.

Ktor Example

In this blog, I’ll show you how you can create an application with endpoints that handle JSON payload.

You can generate a Maven or Gradle project using the Ktor Project Generator web page or directly from within IntelliJ IDEA.

By default, the Netty engine is selected to handle the HTTP request handling but you can select other engines if you like.

Under the plugin section, make sure you select the ‘kotlinx.serialization‘ plugin to have support for JSON. This automatically brings in the routing plugin which is responsible for calling a certain Kotlin function for a URL pattern and the Content Negotiation plugin that sets header values accordingly to the requirements like JSON types.

The generated project contains a main function within the Application.kt file which starts Netty and an extension function that define the configuration of the modules. This allows for a clear separation of each functionality and quickly lets you find each setting.

To have full JSON support, we don’t need to configure anything, just activate the plugin. The Maven plugin in this case makes sure that any class that is annotated with @Serializable can be used at runtime without the need for any reflection. This makes the solution native compile friendly. This a topic we will discuss later in more detail.

For the routing, we call a function that corresponds to the HTTP method we want to support. It has a URL pattern and lambda expression as a parameter. This way, we can define the function for each URL in a very concise and readable way.

    get("/") {
        call.respondText("Hello World!")

The entire example can be found at the Project FF Github repository:


In a time where modular runtimes that are small and take up little memory are important for some people, Kotlin and Ktor are ideal solutions. Kotlin offers many benefits over Java and offers a compact syntax but still highly readable. It supports many important features already for years that are introduced only recently or planned in Java.

Ktor is the natural solution if you choose Kotlin and need to write a backend application. Although you can combine Kotlin with Spring Boot and Quarkus, Ktor is a lightweight solution that has the same functionality available through its modules and is faster at runtime.

Training and Support

Do you need a specific training session on Kotlin, Jakarta EE or MicroProfile? Or do you need some help in getting up and running with your next Ktor project? Have a look at the training support that I provide on the page and contact me for more information.

Atbash Core Profile JAX-RS

Run your Jakarta Application without Runtime

Java EE and now Jakarta EE have the packaging requirements that your application needs to be bundled in a WAR or EAR file and executed by your runtime.
Traditionally, that was the application server that was already configured and running and you deploy the WAR file, containing your application, to it.

These days, with an application runtime, you can launch the runtime and at the same time deploy and run the application by specifying the WAR file location at the command. This makes use of the executable JAR file functionality within Java.

An example of Payara Micro

java -jar payara-micro.jar /path/to/myapplication.war

But now with the addition of the Java SE Bootstrap API to JAX-RS 3.1, this might change. And maybe you don’t need a runtime anymore.

Java SE Bootstrap API

The idea of the Java SE Bootstrap API is that you can start the JAX-RS implementation from the public static void main method. Some of the implementations already had support for this in the past, but it is now available within the JAX-RS specification and thus useable with any certified implementation.

You might wonder how that this is possible without using a servlet container?

When your application or server has REST endpoints that can be called by the client (application), you indeed need to capture the HTTP request. Most of the time, this is done using a servlet container and a specific servlet was responsible to call the correct JAX-RS method depending on the URL.

But actually, you don’t need a servlet for that. Any piece of code that listens on a socket can perform this operation. You don’t need a servlet for this task.

There is even a simple HTTP server within the JDK itself, since Java SE 6, that can be used for this purpose. But also other libraries and products like Netty can be used.

REST without server

Now that you have an API to start your JAX-RS server from within your code, like from within your main method, you can have your application respond to user requests.

The following snippet shows what is needed to realise that.

   SeBootstrap.Configuration.Builder configBuilder = SeBootstrap.Configuration.builder();, "HTTP")
                .property(SeBootstrap.Configuration.HOST, "localhost")
                .property(SeBootstrap.Configuration.PORT, 8080);

   SeBootstrap.start(new DemoApplication(),;

The DemoApplication class is the class that extends the JAX-RS Application class and provides all JAX-RS resource classes through the getClasses() method.

But you probably need more than just the JAX-RS support of Jakarta EE. What about CDI, JSON handling, etc …

Jersey has already an artefact where they integrate Weld, a CDI implementation that also can be started from pure Java SE since CDI 2.0 ‘Java EE 8, and the JSON support, JSON-P and JSON-P, by adding the Jersey media support modules for JSON.

So, you have already all the specifications that make up the Jakarta EE Core Profile, available without the need for any server. An example of such a project can be found in this example project.

Note that this option is different from the embedded server support some vendors provide. There is no specific glue code or additional functionality from the vendor in this case in action. Just some minimal linking between the different specifications.

Really, no server?

Yes, this is now possible and might be the right choice for your situation. It allows you to run your optimised combination of the specifications that you need for the application, right from your main method in Java. Similar to Spring Boot, Jakarta EE can now be used in a truly modular way and no longer require the potentially large set of specifications provided by the runtime that you don’t need.

But you might need to write some classes to better integrate some specification implementations beyond the ones of the Core Profile.

Atbash Runtime

And that is also the goal of the Atbash Runtime, a limited set of code to integrate the specifications seemingness but keep the modular aspect.

That is the reason for the addition of the JakartaRunner class that can be used to start your application from the main method. But at the same time use the module loader of Atbash Runtime so that all specification implementations that are on the classpath are integrated automatically, just like the case with Spring Boot.

It also adds a logging module on top so that you have already one concern less to make your application production worthy.

You can find an example of this approach in this project.


With the addition of the Java SE Bootstrap API to JAX-RS, it becomes now possible to create your own custom, modular runtime that can be started from the main method. This avoids the overhead of potentially many specifications and functionality from the runtime that are not needed for your case.

Atbash Runtime also supports this mode by providing an easy-to-use API that starts the module system of the product behind the scenes and seemingness integrates the different specifications that are found on the classpath.

Want to lean more about Jakarta EE 10 Core Profile?

This topic was discussed during my JakartaOne LiveStream event of 2022. Have a look at the presentation if you want to know more about the Core Profile “Explore the new Jakarta EE Core Profile“.

Atbash Training and Support

Do you need a specific training session on Jakarta or MicroProfile? Or do you need some help in getting up and running with your next project? Have a look at the training support that I provide on the page and contact me for more information.


Atbash JAX-RS MicroProfile

MicroProfile Rest Client for Java SE


One of the cool specifications produced by the MicroProfile group is the Rest Client for MicroProfile  available from MP release 1.3.

It builds on top of the Client API of JAX-RS and allows you to use type-safe access to your endpoints without the need to programmatic interact with the Client API.

MicroProfile compliant server implementations need to implement this specification, but nothing says we cannot expand the usage into other environments (with a proper implementation) like use in Java SE (JavaFX seems must useful here) and plain Java EE.

Atbash has created an implementation of the specification so that it can be used in these environments and will use it within Octopus framework to propagate authentication and authorization information automatically in calls to JAX-RS endpoints.

The specification

A few words about the specification itself. JAX-RS 2.x contains a client API which allows you to access any ‘Rest’ endpoint in a uniform way.

Client client = ClientBuilder.newClient();
WebTarget employeeWebTarget ="http://localhost:8080/demo/data").path("employees");
employeeWebTarget.request(MediaType.APPLICATION_JSON).get(new GenericType<List<Employee>>() {

This client API is great because we can use it to call any endpoint, not even limited to Java ones. As long as they behave in a standard way.

But things can be improved, by moving away from the programmatic way of performing these calls, into a more declarative way.

If we could define some kind of interface like this

public interface EmployeeService {

List<Employee> getAll();

And we just could ask an implementation of this interface which performs the required steps of creating the Client, WebTarget and invoke it for us in the background. This would make it much easier for the developer and makes it much more type-safe.

Creating the implementation of that interface is what MicroProfile Rest Client is all about.

The interface defined above can then be injected in any other CDI bean (need to add the @RegisterRestClient on the interface and preferably a CDI scope like @ApplicationScoped) and by calling the method, we actually perform a call to the endpoint and retrieve the result.

private EmployeeService employeeService;

public void doSomethingWithEmployees() {
... employeeService.getAll();

Atbash Rest Client

The specification also allows for a programmatic retrieval of the implementation, for those scenarios where no CDI is available.
However, remember that if we are running inside a CDI container, we can always retrieve some CDI bean by using


from within any method.

The programmatic retrieval is thus an ideal candidate to use it in other environments or frameworks like JavaFX (basically every Java SE program), Spring, Kotlin, etc …

The programmatic retrieval starts from the RestClientBuilder with the newBuilder() method.

EmployeeService employeeService = RestClientBuilder.newBuilder()
    .baseUrl(new URL("https://localhost:8080/server/data"))

The above retrieves also an implementation for the Employee retrieval endpoint we used earlier on in this text.

For more information about the features like Exception handling, adding custom providers and more, look at the specification document.

The Atbash Rest Client is an implementation in Java 7 which can run on Java SE. It is created mainly for the Octopus Framework to propagate the user authentication (like username) and authorization information (like permissions) to JAX-RS endpoints in a transparent, automatically way.

A ClientRequestFilter is created in Octopus which creates a JWT token (compatible with MicroProfile JWT auth specification) containing username and permissions of the current user and this Filter can then be added as a provider to the MicroProfile Rest Client to have this security information available within the header of the call.

Since the current Octopus version is still based on Java SE 7, no other existing implementation could be used (MicroProfile is Java SE 8 based). The implementation is based on the DeltaSpike Proxy features and uses any Client API compatible implementation which is available at runtime.

Compliant, not certified.

Since all the MicroProfile specifications are Java 8 based, the API is also converted to Java SE 7. Except for a few small incompatibilities, the port is 100% interchangeable.

The most notable difference is creating the builder with the newBuilder() method. In the original specification, this is a static method of an interface which is not allowed in Java 7. For that purpose, an abstract class is created, AbstractRestClientBuilder which contains the method.

Other than that, class and method names should be identical, which makes the switch from Atbash Rest Client to any other implementation using Java 8 very smooth.

The Maven dependency has following coordinates (available on Maven Central):


If you are running on Java 8, you can use the Apache CXF MicroProfile Rest client


Which also can be used in a plain Java SE environment. Other implementations like the ones within Liberty and Payara Micro aren’t useable standalone in Java SE.


This first release of Atbash Rest Client is a pre-release, meaning that not all features of the specification are completely implemented. It is just a bare minimum for the a POC within Octopus.
For example, the handling of exceptions (because the endpoint returned a status in the range 4xx or 5xx) isn’t completely covered yet.

The missing parts will be implemented or improved in a future version.


With the MicroProfile Rest Client specification, it becomes easy to call some JAX-RS endpoints in a type-safe way. By using a declarative method, calling some endpoint becomes as easy as calling any other method within the JVM.

And since micro-services need to be called at some point from non-micro-service code, Java SE executable implementation is very important. It makes it possible to call it from plain Java EE, Java SE, any other framework or a JVM based language.

Have fun.

JAX-RS Security

Http Signatures for End-To-End protection


In a distributed system, it is not enough to know who called you, but you also need to make sure the data which you receive are not altered in transit.
The use of tokens (like Bearer JWT tokens according to the OpenId Connect or MicroProfile JWT Auth specifications) can be captured and briefly used to send fake request to your endpoints.
And SSL (like using HTTPS connections) cannot guarantee that your message isn’t read or altered by someone due to the possibilities of SSL Proxy termination or even Man In The Middle Intermediates who fakes correct SSL.

Encryption or protection

The solution is that your processes somehow make sure that the communication between them is ‘safe’. If the processes themselves (your client and your server application) perform these tasks and not some network layer (SSL, in fact, doesn’t fit in any OSI based network layer) of your (virtual) machine.

When your process is performing these tasks, it is possible to create a security context between the 2 processes and then each process can ensure or detect that the message is unaltered or can’t be read, regardless of any Proxy or Man In The Middle.

Whether you need encryption of the message (because you have confidential data) or signing (sensitive data) depends on the data. But at least you need the signing of the message so that we can guarantee the message is actually sent by someone we trust and isn’t altered.

Http Signatures

There is a standard created to have a mechanism to implement this kind of End To End Protection. It is called ‘HTTP Signatures’ and designed by the IETF.

It is very HTTP friendly as it adds an additional header to the request. Within this header, enough information is placed so that the target process can verify if the message was altered or not.

This header is called Signature

Signature : keyId="rsa-key-1",algorithm="rsa-sha256",headers="(request-target) date digest content-length",signature="Base64(RSA-SHA256(signing string))"

The specification defines the structure and framework how the header should be created and the verification process must be performed but leaves a lot of freedom to the developer to tailor this protection to his/her needs.

The idea is that there is a signature calculated based on some parameters which are mostly just other headers. And by calculating them on the receiving side, we can verify if these values are changed.

These parameters are described within the headers parameter, and in the example, the following headers are used (request-target) date digest content-length

The values of these headers are concatenated (there exist a few rules about how to do it) and for this string, the cryptographic hash is calculated the signature.
The calculation is done using the value specified by the algorithm parameter (rsa-sha256 in our example above) and from the key with id rsa-key-1 is used for this in our example.
There are a number of crypto algorithms allowed, but RSA keys are the most popular ones.
This signature value is a binary, so a BASE64 encoded value is placed within the header.

In theory, all headers can be used but some of them are more interesting than other ones.
For example

  • (request-target) is a pseudo header value containing the concatenation of the http method and the path of the URL. If you like to include also the hostname (which can be interesting if you want to make sure that a request for the test environments never hits production, you can add the host header in the set of headers which needs to be used for the calculation of the signature value.
  • date header contains the timestamp when the request is created. This allows is to reject ‘old’ requests.
  • digest is very interesting when there is a payload send to the endpoint (like with put and post). This header contains then the hash value of the payload and is a key point in the end to end protection we are discussing here.

On the receiving side, the headers which are specified within the signature parameter are checked. Like

  • date is verified to be within a skew value from the system time
  • digest is verified to see if the header value is the same as the calculated hash value from the payload.

And of course, the signature is verified. Here we decode the signature value with the public key defined in the keyId parameter, using the algorithm specified.
And that value should match the calculated value from the headers.

Using Http Signatures

I have created a Proof Of Concept how this can be integrated with JAX-RS, client and server side.

For the server side, it is quite simple since you can add filters and interceptors easily by annotating them with @Provider. By just then indicating if this interceptor and filters should need to do their work, verification of the HTTP Signature, you are set to go.

public class OrderController {

On the client side, you can register the required filters and WriterInterceptor manually with the Client. Something like this

Client client = ClientBuilder.newClient();

One thing is on the roadmap is to integrate it with the MicroProfile Rest Client way of working so that you can register the Signatures Feature and you’re done.

The initial code can be found on Github.


With the Http Signatures specification, you can add a header which can be used to verify if the message is changed or not. This is a nice non-intrusive way of having an End-To-End protection implement which can complement the SSL features to be sure that no intermediary party changes the message.

Have fun.

This website uses cookies. By continuing to use this site, you accept our use of cookies.  Learn more