Over a million developers have joined DZone.

Groovy++ in action: DSL for embedding HttpServer

· Java Zone

Easily build powerful user management, authentication, and authorization into your web and mobile applications. Download this Forrester report on the new landscape of Customer Identity and Access Management, brought to you in partnership with Stormpath.

JDK6 introduced great but probably not too widely known possibility of embedding lightweight http server in to your application. If you are not aware about it as I was here is the link for javadocs of simple and nice API Of course, several great options for embedded http server (servlet container usually) existed before. Jetty was my favorite for a long time. The biggest difference is that JDK gives you something which is really minimalistic but extremely easy and lightweight. For sure, it is not good as replacement of production application server but just perfect for example as remote control for an application.

My personal use case is the following: I have several machines running on EC2. Instead of having dozen of different shell scripts to be run via terminal I have one Groovy script which starts simple web server on each machine. All the rest done via browser window.

In this article I will explain how I wrap http server API in to Groovy++ DSL

Let us start with simplest possible "Hello, World!" server.

def logger = Logger.getLogger(this.class.name)
httpServer(8081) { exchange ->

exchange.sendResponseHeaders(200,0)
exchange.responseBody.withPrintWriter { out ->
out.println "Hello, World!"
}

logger.info """URI: ${exchange.requestURI}
${exchange.requestHeaders}"""
}

What the server above does is reply with text "Hello, World!" to any request and logs the request URI and headers. Very simple. Probably too simple even for simplest real application. But before moving to something more real let us see what method httpServer does.

static HttpServer httpServer(int port = 8080, HttpHandler handler) {
httpServer(port) { ->
context("/", handler)
}
}

What we see here is that the method takes two parameters: port number and instance of a class implementing HttpHandler interface from JDK.

HttpHandler is so called Single Abstract Method(SAM) class. That allows Groovy++ compiler to instantiate it from closure expression without any problem. We will use this observation several times in this article.

As we see implementation of method httpServer calls another method with the same name but different arguments.

static HttpServer httpServer(int port = 8080, ServerDefinition config) {
def server = HttpServer.create(new InetSocketAddress(InetAddress.localHost, port), 0)
config.server = server
config.define()
server.start()
server
}

The method above also takes two parameters: port number and ServerDefinition. ServerDefinition is SAM, which is immidiately called after server created and should configurate all necessary parameters(http contexts with respective handlers, both global and per context filters and autentificator) 

Careful reader can be intrigued at this point how does compiler decide if closure expression represents ServerDefinition or HttpHandler

In fact it is very easy: by parameters of the closure expression. The only abstract method of HttpHandler has one parameter of type HttpExchange and the only abstract method of ServerDefinition does not have parameters.

Here is the same "Hello, World!" example rewritten in more canonical form. We define global filter, which will be applied to all http contexts in our server, and the only http context on our server, which replies "Hello, World!" to any request.

httpServer(8082) { ->
filter { exchange, chain ->
logger.info """URI: ${exchange.requestURI}
${exchange.requestHeaders}"""
}

context("/") { exchange ->
exchange.sendResponseHeaders(200,0)
exchange.responseBody.withPrintWriter { out ->
out.println "Hello, World!"
}
}
}

Now we are almost ready to implement ServerDefinition class. But before that let us create common super class for both ServerDefinition and ContexDefinition (we will talk about it a bit later) and specialized implementation of Filter

abstract class DefaultFilter extends Filter {
String description = "Filter ${this.class.name}"

String description () {
description
}
}

abstract class FiltersDefinition {
protected List<Filter> filters = []

void filter (String description = null, DefaultFilter filter) {
if (description)
filter.description = description
filters << filter
}
}

Why do we need specialized implementation of Filter?

Filter is not SAM, so we can't use closure expression, when need to implement it. Normally [:] Groovy++ syntax would do the job but in our particular case more elegant solution above is possible.

Now let us have a look on implementation of ServerDefinition

abstract class ServerDefinition extends FiltersDefinition {
private HttpServer server

abstract void define ()

void context(String path, ContextDefinition contextConfig) {
def context = server.createContext(path)
contextConfig.context = context
context.filters.addAll (filters)
contextConfig.define()
context.filters.addAll (contextConfig.filters)
context.handler = { exchange ->
def method = exchange.requestMethod
def handler = contextConfig.methods[method]
if (handler)
handler.handle(exchange)
else {
exchange.sendResponseHeaders(404,0)
def out = new PrintStream(exchange.responseBody)
out.print "HTTP method ${method} is not supported"
out.close ()
}
exchange.close ()
}
}

void context(String context, HttpHandler handler) {
def httpContext = server.createContext(context)
httpContext.filters.addAll (filters)
httpContext.handler = { exchange ->
try {
handler.handle(exchange)
}
finally {
exchange.close ()
}
}
}
}

We already saw in action the second variant of method context. It takes context path and HttpHandler as parameters. Handler does real job but we wrap it in to try/finally block to make sure that our processing will be finished correctly.

Notice how elegantly looks wrapper closure compare to regular Java anonimous inner class

The first variant of context method is a little bit more involved. Before we explain what ContextDefinition is probably it will be easier to show how we will use it.

The following piece of code implements simple service, which allows clients to put/get key/value pairs in to the server. Keys are strings and values are any binary data. Keys will be requested in the form "/map/<key>". To retrieve data http GET method will be used and the method PUT to put a data.

    context("/map/") { ->
@Field ConcurrentHashMap<String, byte[]> data = []

filter { exchange, chain ->
String key = exchange.requestURI.toString().substring(exchange.httpContext.path.length())
exchange.setAttribute("key", key)
if (key)
chain.doFilter(exchange)
else {
exchange.sendResponseHeaders(404,-1)
exchange.close ()
}
}

get { exchange ->
String key = exchange.getAttribute("key")
def result = data[key]

exchange.sendResponseHeaders(200,4 + result?.length)

exchange.responseBody.withDataOutputStream { out ->
if (result) {
out.writeInt(result.length)
out.write(result)
}
else {
out.writeInt(-1)
}
}
}

post { exchange ->
exchange.sendResponseHeaders(200,-1)
String key = exchange.getAttribute("key")

exchange.requestBody.withDataInputStream { input ->
def len = input.readInt ()
if (len < 0)
data.remove(key)
else {
def bytes = new byte [len]
input.read(bytes)
data.put(key, bytes)
}
}
}
}

Now I recommend to review again first version of ServerDefinition.context method. What's goinng on there is we choose handler based on http method used

 To complete our story we need to define ContextDefinition class. It is really simple.

abstract class ContextDefinition extends FiltersDefinition {
protected HttpContext context

abstract void define ()

Map<String,HttpHandler> methods = [:]

void method(String method, HttpHandler action) {
methods[method] = action
}

void get(HttpHandler action) {
method("GET", action)
}

void post(HttpHandler action) {
method("POST", action)
}

void setAuthenticator(Authenticator authenticator) {
context.setAuthenticator(authenticator)
}
}

We are done and I hope you would agree that embedding of HttpServer shipped with JDK6 is not complex at all. If you would also like to learn more more about Groovy++ DSLs then I don't mind spending part of my weekend writing this article :)

Till next time.

The Java Zone is brought to you by Stormpath—a complete, pre-built User Management API. Want to learn how to use JWTs to protect microservices from CSRF and more? Check out this on-demand webinar with our Java Developer Evangelist, Micah Silverman.

Topics:

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}