Using different context handlers on different ports in Jetty 9

Today it’s popular to embed containers such as Jetty into the application itself so that it’s simple to start and distribute in, for example, a microservice architecture. But there are some things to consider when doing this. You probably want some health checks, metrics and other administrative resources that you don’t want to expose to the outside world. It’s a good idea to for deploy these on a different port than the external resources so that they can be protected by for example an AWS security group. Using something like Dropwizard gives you this capability out of the box which is very nice but sometimes you can’t use Dropwizard for various reasons (for example Dropwizard currently depends on an old version of Jersey which doesn’t let you use asynchronous request processing).

How to go about

Starting an embedded Jetty container quite simple, usually you do something like this and you’re more or less done:

This will start a Jetty server on port 8080 and expose the MyJerseyResource to the world. So now let’s add our health check resource, the naive way would be to do something like this:

However now the entire world will have access to the health check resource which is unnecessary. To allow the devops to easily restrict the access we want to deploy the HealthCheckResource on a different port. To do this in Jetty we need to modify our server to use two different ServerConnector‘s, one for the administrive part and one for the external part. For example:

Setting a name is very important and the reason for this is that we also needs two different ServletContextHandler‘s:

Note the last line, adminContext.setVirtualHosts(new String[]{"@Admin"});, this is what will make the adminContext use port 8081 by refering to the adminConnector defined earlier. We prefix the name with an @ because this is the way Jetty finds a named connector in version 9 (in Jetty 8 you would instead use adminContext.setConnectorNames(new String[]{"Admin"}); but this option is no longer available in Jetty 9).

Heroku deployment

While this works fine in most cases there are scenarios where you might still need to deploy the administrative and external resources to the same port. One example is if you want to deploy on Heroku which only allows you to use a single port. To achieve this we’re going to use a “trick” from Dropwizard and assign the adminContext to the same port as the externalContext if the external and admin ports are configured to the same value. In order to differentiate between the admin and external resources we want to change the context path for the admin resources to something else, for example “/admin”. Since we’re using the same port we don’t need to create a new connector for the admin resources. However we need an if-statement to determine to which virtual host we should add the adminContext:

Conclusion

At the time of writing the Jetty 9 documentation was not quite up to date so I actually had to dig into the Jetty source code to find out some of the things mentioned this blog post. A full example is shown here for clarity:

Note that in Jetty 9.1 it seems like you must add the adminContext handler before the externalContext handler to the HandlerCollection (collection) otherwise it won’t work.

That’s it!

This Post Has 2 Comments

  1. Hi,
    Very informative blog post. I am trying to achieve same thing but I have a standalone jetty server. I am able to create connector by going through jetty documentation. But I am not sure about how/where to specify settings about serving certain servlet using certain connector.
    Currently I am deploying my application as a war file by putting it into webapp directory of jetty. One way to achieve this would be to duplicating the war file and essentially running two applications, but this is not something I want. Can you shed some light on how to achieve this in standalone jetty?

    Thanks,
    Satish

  2. Hi,
    Very informative blog post. I am trying to achieve same thing but I have a standalone jetty server. I am able to create connector by going through jetty documentation. But I am not sure about how/where to specify settings about serving certain servlet using certain connector.
    Currently I am deploying my application as a war file by putting it into webapp directory of jetty. One way to achieve this would be to duplicating the war file and essentially running two applications, but this is not something I want. Can you shed some light on how to achieve this in standalone jetty?

    Thanks,
    Satish

Leave a Reply

Close Menu