Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Erik Lindblom
Erik Lindblom

Posted on • Originally published atblog.baens.net on

     

Step-by-step: Kotlin, JAX-RS (Jersey), and Docker

This was originally published on mypersonal blog

Hello Again and welcome to another blog post in my ongoing series on step-by-step tutorials. This one will actually build atop the lastHello Kotlin one and make aJAX-RS web service. This particular implementation of JAX-RS will beJersey. Jersey is actually the reference implementation from Oracle, but there are a few out there that I've honestly haven't given enough time.

Step 0: Prerequisites

For this one, I am going to start off with my Kotlin Hello World repository. This already has a Gradle wrapper setup, along with some basic things. I've changedmainClassName toServer.kt andarchivesBaseName to match this projects name.

Step 1: Web Server

First step will be to get a working web server up and running. This project will be using the grizzly web server, which I recommend, as the embedded web server. It has a decent enough history of performance and from my usage it does a decent job.

So let's take a look at how a quick server can be setup so we can verify we have the HTTP pipeline correct.

src/Server.kt

importorg.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactoryimportjavax.ws.rs.core.UriBuilderfunmain(args:Array<String>){valurl=UriBuilder.fromUri("http://0.0.0.0/").port(8080).build()valhttpServer=GrizzlyHttpServerFactory.createHttpServer(url,true)if(System.getenv()["SHUTDOWN_TYPE"].equals("INPUT")){println("Press any key to shutdown")readLine()println("Shutting down from input")httpServer.shutdownNow()}else{Runtime.getRuntime().addShutdownHook(Thread{println("Shutting down from shutdown hook")httpServer.shutdownNow()})println("Press Ctrl+C to shutdown")Thread.currentThread().join()}}

Line 6: This creates a URI for the server to bind to. This particular one makes it sohttp://localhost:8080 will work. Probably overkill but that is what the Grizzly Server factory wants.

Line 9: This creates the Grizzly Server and the 2nd parameter (true) makes the server start immediately.

Lines 14 - 27: Now, let me explain this because this is sort of magic. When running under Gradle (./gradlew run), Gradle captures all of the input and passes it along to the application. So for example, on the command line you usually stop applications by hittingCtrl+C. This however doesn't work when you run it under Gradle because Gradle captures the signal and stops the daemon that is running the application. Sometimes, the Gradle daemon doesn't stop properly, or clean everything up and produces a mess the next go around.

So my hack to fix all of this was to have a feature flag in an environment variable. When running under Gradle (hold on one sec and I'll show you how) we will just wait for input. When running under everything else (i.e. docker,java -jar) we will wait for Ctrl+C or a kill signal from somewhere else.

Now for the toolchain bits, we need to add the dependencies and setup the run bits to work with out shutdown hooks.

build.gradle (only parts)

...dependencies{compile'org.jetbrains.kotlin:kotlin-stdlib-jre8'compile"org.glassfish.jersey.containers:jersey-container-grizzly2-http:2.26"}...// Properties for `./gradlew run`run{standardInput=System.inenvironment=["SHUTDOWN_TYPE":"INPUT"]}

Line 4: We add the grizzly http server and Jersey container.

Line 8 - 11: This is how we make./gradlew run work. ThestandardInput we set to the normalSystem.in (it defaults to an empty set which breaks things). Then we setup the environment variables with theenvironment configuration.

We need to add a way for the port to be exposed by Docker to the outside world. Since we are binding on port 8080 we need to have that exposed as well. So in theDockerfile we will add one little line before the CMD statement.

Dockerfile (only parts)

EXPOSE8080CMD["java","-jar","run.jar"]

So verify the two ways to run the application and viewhttp://localhost:8080.

First run./gradlew run and if you hit any key, it will shutdown.

Second run the docker commanddocker build -t myserver . && docker run -p 8080:8080 myserver. This you can run and then hitCtrl+C to shut it down.

Quick aside on the docker command: there are two parts to it, first you build the image you want to run, then you run it. The-t you pass in the build command is a tag that makes it easier to look up later. In the run part the-p is the ports we will bind to the host. For this example we are taking the containers's 8080 port mapping it to the hosts 8080 port.

Step 2: Add Simple JAX-RS endpoint that prints "Hello World"

For this let's create the needed pipeline to get the needed resources. There is a little bit of yak shaving needed for this setup to get all of the right pieces in the place but I think I've boiled it down to the simple parts.

First let's see what the resource (these are what the groups of end points are called) look like for our first end point.

src/jaxrs/resources/HelloWorld.kt

packagejaxrs.resourcesimportjavax.ws.rs.GETimportjavax.ws.rs.Path@Path("helloWorld")classHelloWorldResource{@GETfunhelloWorld()="Hello World"}

There really isn't much to talk about here. The@Path is where the end point will be (so in this case/helloWorld). This will be retrieved from a HTTP GET.

Alright, the next step is to get the system setup to register these resources automatically. So let's setup the config needed for that.

packagejaxrsimportorg.glassfish.jersey.server.ResourceConfigclassApplication:ResourceConfig(){init{packages("jaxrs.resources")}}

Again, this is fairly straight forward. TheResourceConfig is where all of base application is setup. In this example. we scan thejaxrs.resources namespace for any resources or other things that can register with JAX-RS. This is a specific to Jersey but it makes it easier to setup things.

Alright, now that we have all of the configuration setup for JAX-RS and a resource, lets make the server recognize it. So to theServer.kt file added this.

Server.kt (parts)

valhttpServer=GrizzlyHttpServerFactory.createHttpServer(url,Application(),true)

Line 12: This is all you need to add. A new instance of theApplication class and the Grizzly server does all the rest for you.

Now, one last thing, we need to add a dependency for Jersey. This one will make all of the magical dependency injection happen behind the scenes. The Jersey team have made this configurable dependency, so if you want a different type of injection system you can. The specific one implementation we will use isHK2 as opposed toGuice or others. This was just the default that I've used and have had no problems with it so I recommend it. So here is the line that needs to be added.

build.gradle (parts)

dependencies{compile'org.jetbrains.kotlin:kotlin-stdlib-jre8'compile"org.glassfish.jersey.containers:jersey-container-grizzly2-http:2.26"compile"org.glassfish.jersey.inject:jersey-hk2:2.26"}

Go ahead and run to see the output onhttp://localhost:8080/helloWorld.

Step 3: Add JSON handling

Now that we have a simple web server up, let's get JSON handling. This isn't configured out of the box but it is straight forward. So let's get it setup and working and have an end point setup to demonstrate that.

Let's setup our resource to have the parts needed to show JSON output.

src/jaxrs/resources/HelloWorld.kt

@GETfunhelloWorld()="Hello World"data classHelloJson(valprop1:Int,valprop2:String)@GET@Path("json")@Produces(MediaType.APPLICATION_JSON)funhelloJson()=HelloJson(1,"test")

Line 13: This is why I love Kotlin, this is a data class. A beautifully pure and simple structure to pass data around. We have two properties that will be exposed to the JSON interface. One an integer, the next a string.

Lines 15-18: This is how you setup an endpoint that will produce a JSON object (well, force it too, but that's for another day, or go read theJersey documentation). We simply just return an instance of theHelloJson data class.

Now, we need to tweak our dependencies and add the JSON handling. While we do this, we will also be violating theRule of 3, and we are going to refactor out the common things that may change. Let's see the result and we will talk about what exactly that means.

build.gradle (parts)

defjerseyVersion="2.26"dependencies{compile'org.jetbrains.kotlin:kotlin-stdlib-jre8'compile"org.glassfish.jersey.containers:jersey-container-grizzly2-http:${jerseyVersion}"compile"org.glassfish.jersey.inject:jersey-hk2:${jerseyVersion}"compile"org.glassfish.jersey.media:jersey-media-json-jackson:${jerseyVersion}"}

Line 22: We added this line because the parts that could change between all of these jersey dependencies is the Jersey version. So let's do the work now to make it so we can make the version change in one place. A small application of the Rule of 3, but hey, it works.

Line 28: This is addingJackson to do our JSON processing. This is the magic dependency that we will need to make this all work.

At this point you can now run it and hithttp://localhost:8080/helloWorld/json and you should see JSON output.

And with that, you should now have the basics to be dangerous with. You can setup a HTTP REST server and go to town with what ever you need. But hey, let me throw in one more little bonus step that may help you out.

Bonus Step 4: Add verbose logging

Just to make it easier to see requests coming in and out of your service on the command line. Which can help with proving you are either insane or sane depending on the circumstances, let's add a logger that will output each request.

Thankfully this fairly straight forward. We just need to tweak theApplication.kt setup. So here is what you need.

src/jaxrs/Application.kt

register(LoggingFeature(Logger.getLogger(LoggingFeature.DEFAULT_LOGGER_NAME),Level.INFO,LoggingFeature.Verbosity.PAYLOAD_ANY,Integer.MAX_VALUE))

These are the lines you need to add to theinit block. Fairly straight forward (make sure to add all of the rightimport statements!). This should now display all of the requests and responses on the command line so you can verify things are going in and out as expected.

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

Just a developer looking to help solve the world's problems with code.
  • Location
    Salt Lake City, Utah
  • Education
    BS in CS from Westminster College
  • Work
    Principal Developer at Medici Ventures
  • Joined

More fromErik Lindblom

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp