Undertow is a lightweight HTTP and WebSocket server for Java. It’s super simple to set up and works with Java SE. I tried using it in my open source file sync tool Syncany and it works beautifully.
Content
1. Undertow is awesome!
I (plan to) use it for three things:
- As a WebSocket server for IPC and GUI-to-server communication
- To allow a pluggable web interface (on the same port)
- And to additionally provide a RESTful API for clients that don’t speak WebSocket
I already sort of implemented (1) and (2) in a branch. Initializing Undetow is pretty easy:
1 2 3 4 5 6 7 |
Undertow webServer = Undertow.builder() .addHttpListener(8080, "localhost") .setHandler(path() .addPrefixPath("/api/ws", websocket(new InternalWebSocketHandler())) .addPrefixPath("/api/rest", new InternalRestHandler()) .addPrefixPath("/", new InternalWebInterfaceHandler()) ).build(); |
This initializes Undertow at port 8080 and binds it only to localhost. With a path-based request handler, it allows you to add prefixes using addPrefixPath() and assign a request handler based on this path. You can either implement these HttpHandlers yourself, or you can use the prerolled handlers for common use cases.
There are plenty of prerolled HTTP handlers, e.g.
- to handle path-based routing to differ route based on path, such as you’ve seen above; see path() aka the PathHandler
- to route requests based on virtual hosts (how awesome is that?); see virtualHost() aka the NameVirtualHostHandler
- to control access to web methods or resource via an access-control handlers (based on IP address or ACLs); see acl()/ipAccessControl() aka AccessControlListHandler/IPAddressAccessControlHandler
- to deliver static resources such as HTML pages, stylesheets or JavaScript; see resource() aka the ResourceHandler
- and many more!
For my use case, the most useful ones are path(), resource() and websocket(). path() routes incoming requests to the REST-API, the web interface or the WebSocket handler. resource() delivers the static web interface, and websocket() provides the entry point for the WebSocket handshake (you know, the HTTP/1.1 101 Switching Protocols stuff).
2. Undertow speaks WebSocket!
I don’t know if there are any other embeddable open-source Java SE WebSocket+HTTP servers, but Undertow certainly fulfills my requirements. I tried to get TooTallNate’s Java-WebSocket (also a very well written and perfectly working piece of software), but it can only do WebSockets, and no plain HTTP (other than the WS handshake).
For Syncany, I use WebSockets to receive GUI requests and send asynchronous responses. All you have to do is to implement a single callback handler and you’re good to go:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
private class InternalWebSocketHandler implements WebSocketConnectionCallback { @Override public void onConnect(WebSocketHttpExchange exchange, WebSocketChannel channel) { channel.getReceiveSetter().set(new AbstractReceiveListener() { @Override protected void onFullTextMessage(WebSocketChannel clientChannel, BufferedTextMessage message) { handleMessage(clientChannel, message.getData()); } // ... }); channel.resumeReceives(); } } |
If you connect to http://localhost:8080/api/ws, the PathHandler will route the request to the WS handler and the onConnect() method will be called once the WS handshake was successful. You can see working code on GitHub.
3. Delivering static resources
Delivering static content is reeeally easy! The ResourceHandler allows you to define file resources (on the local file system), URL resources, or Java resources. For my use case, the web front-end is plain HTML+JS, so the resource handler just points to a Java resource package:
1 2 3 |
resource(new ClassPathResourceManager(ThisClass.class.getClassLoader(), "/org/syncany/plugins/web/simple")) .addWelcomeFiles("index.html") .setDirectoryListingEnabled(true); |
Awesome, right?
4. Links
The documentation of Undertow is not so great at the moment, but there are a couple of code examples on GitHub.
A. About this post
I’m trying a new section for my blog. I call it Code Snippets. It’ll be very short, code-focused posts of things I recently discovered or find fascinating or helpful. I hope this helps.
Seems like a lot of work headed your way. You’ll have to write all the routing logic, and then come up with a way to glue your application code together, if you are using a database you’ll have to configure a connection pool some how, then figure out how to manage transactions, next figure out security around authorization, you’ll need a mechanism to manage different configurations for the different environments that you need to deploy too. Not seeing the benefits at this point.