Running Play Framework on NixOS using JDK 11
nixos play-framework sbt scalaGood morning! First, some personal news: I'm switching to NixOS, and I'm kind of excited about it, so expect some articles to that effect. Today, I'm celebrating the ease with which I got a Play Framework environment up and running, including installing sbt and downgrading to JDK 11.
As a software developer, I do not at appreciate having to fight with my environment, so when I heard that NixOS uses a purely functional, declarative approach to maintaining packages, I was interested. Recently, my approach maintaining a clean desktop has revolved around docker, so I'm open to less hacky approaches for sure.
The real test is sbt, which in my experience is a finicky piece of software. Anything with a Java dependency can get complicated already, and sbt is particularly stateful as it downloads its dependencies on the fly. So far I haven't been successful getting the Docker-and-alias trick above to work.
Installing sbt
on NixOS
...is pleasantly trivial. There is an
sbt
package in the official
repository
which can be installed in the usual NixOS
way in the packages list in the /etc/nixos/configuration.nix file.
/etc/nixos/configuration.nix
...
environment.systemPackages = with pkgs; [
docker
emacs
firefox
git
gnome.gnome-tweaks
sbt
wget
];
...
Then, to make the change take effect, run sudo nixos-rebuild switch
.
After this, I can run sbt
against my existing project and it works smoothly.
But it's not compatible with Play at runtime.
UncheckedExecutionException: java.lang.IllegalStateException: Unable to load cache item
When I run the application, it serves exceptions:
console output
--- (Running the application, auto-reloading is enabled) ---
p.c.s.AkkaHttpServer - Listening for HTTP on /[0:0:0:0:0:0:0:0]:9000
(Server started, use Enter to stop and go back to the console...)
2022-07-04 12:46:00 ERROR p.api.http.DefaultHttpErrorHandler
! @7o7k89hhe - Internal server error, for (GET) [/] ->
play.api.UnexpectedException: Unexpected exception[UncheckedExecutionException: java.lang.IllegalStateException: Unable to load cache item]
at play.core.server.DevServerStart$$anon$1.reload(DevServerStart.scala:254)
at play.core.server.DevServerStart$$anon$1.get(DevServerStart.scala:148)
at play.core.server.AkkaHttpServer.handleRequest(AkkaHttpServer.scala:302)
at play.core.server.AkkaHttpServer.$anonfun$createServerBinding$1(AkkaHttpServer.scala:224)
at akka.stream.impl.fusing.MapAsync$$anon$30.onPush(Ops.scala:1307)
at akka.stream.impl.fusing.GraphInterpreter.processPush(GraphInterpreter.scala:542)
...
stack trace from error page
com.google.common.util.concurrent.UncheckedExecutionException: java.lang.IllegalStateException: Unable to load cache item
com.google.common.cache.LocalCache$Segment.get(LocalCache.java:2051)
com.google.common.cache.LocalCache.get(LocalCache.java:3962)
com.google.common.cache.LocalCache.getOrLoad(LocalCache.java:3985)
com.google.common.cache.LocalCache$LocalLoadingCache.get(LocalCache.java:4946)
com.google.common.cache.LocalCache$LocalLoadingCache.getUnchecked(LocalCache.java:4952)
com.google.inject.internal.FailableCache.get(FailableCache.java:54)
com.google.inject.internal.ConstructorInjectorStore.get(ConstructorInjectorStore.java:49)
com.google.inject.internal.ConstructorBindingImpl.initialize(ConstructorBindingImpl.java:155)
com.google.inject.internal.InjectorImpl.initializeBinding(InjectorImpl.java:592)
Both stack traces given go on for ages winding through various Play, Akka, Google, and even core Java libraries, far downstack from the actual application code.
Play Framework Compatibility
A quick search tells me that other people have this problem when running
Play Framework against incompatible Java versions and that Play Framework 2.8
(the current version at the time of this writing) is compatible with JDK 8 and
JDK 11. Sure enough, when I watched sbt
startup, I saw that it used
JDK 17, so
I knew I needed to downgrade to JDK 11.
I tried installing the JDK 11 package using the same /etc/configuration.nix, but of course it's a feature of Nix that that's not how that works--each package comes with it's own dependencies, and sbt uses JDK 17 still.
So the question is: How do I downgrade the JDK version that sbt uses from JDK 17 to JDK 11?
And this makes me a little itchy. Is this going to be hard? Am I going to have to fork the sbt package? Because the reason why I switched to NixOS in the first place is to avoid going to such lengths.
Downgrading the JDK version used by sbt
It turns out it's easy. The Java version used by the sbt package can be overridden declaratively within that same line in the configuration file.
/etc/nixos/configuration.nix
...
environment.systemPackages = with pkgs; [
docker
emacs
firefox
git
gnome.gnome-tweaks
(sbt.override { jre = pkgs.jdk11; })
wget
];
...
Then just nixos-rebuild switch
and you're good to go!
In Conclusion... I think I like this!
On the one hand, I've been an Ubuntu user for almost ten
years. I know how to be productive there. When things
break, I can generally fix them, and if I can't, I can
generally figure out what would be good search terms to
start with. On the other hand, I think I'm just getting a glimpse
of just how powerful nix
really is. If I can
make a perfectly reproducible Scala environment with a
single line, that alone might be worth sticking around
for. More to come, I'm sure.
I write to learn, so I welcome your constructive criticism. Report issues on GitLab.