Show HN: I finished v5 of a JVM framework I've spent spent half a decade making
javalin.io
javalin.io
I ended up using Javalin with Kotlin and SQLite to build my wedding website.
Most of my projects are lucky to see the light of day, but with a very persistent project manager and a simple lightweight framework, I was able to ship a fantastic product with very little fuss.
> but with a very persistent project manager
Your partner? :D
Benefits:
Unbelievably fast startup time
No magic
No annotations, XML or YAML required
No classpath scanning related security vulnerabilities
Conpletely deterministic
In no other language is ‘calling a constructor’ considered so complicated a framework is required.
Try it!
It does help to keep services small so the manual DI arg passing doesn’t get too spread out and boilerplate-y. But I have worked on a dozen services here and don’t miss an automatic DI framework.
I think it helps if you are building test/fakes of your deps that don't really need as many sub-dependencies. Generally you in a test `@BeforeAll` construct a bunch of objects or for a program you do it in `main`.
I did work on a project while I was at Google that was really big and a decade old that instead had a class with a bunch of lazy getters for each item in the dependency graph, then tests would override or reset the getters with testing versions as needed. It was a little clunky but I preferred that approach over the projects I worked on that used dagger.
If you've read through the Dagger dev-guide [0] there is *a lot* in there, while the manual approach it's usually just `new` which is a really simple concept on it's own :) I think this is the right tradeoff because reading code is harder than writing code, so it's worth the extra setup. In practice I haven't seen it to amount to be an overwhelming amount.
This feels like the sane thing to do, but sadly for historical reasons this won't work with many (if not the most) Java frameworks out there, or even certain options in the .NET ecosystem.
When you get non-trivial frameworks that are deeply entrenched in having DI and using it internally for a bunch of stuff, you'll find yourself out of options, e.g. if you use Spring/Spring Boot.
And in many places something like Spring Boot might be considered an "industry standard" and you might get looked at funny if you suggested using another framework, even more so if they have their own configuration/plugin/utility solutions for it already built within the org.
Personally, however, I rather enjoy alternative takes on how web frameworks could look and even something like Dropwizard/Vert.X/Quarkus have nice quality of life aspects to them.
In a sense, it's nice that the industry is moving towards smaller service based architectures, where you might spin up a new service with whatever tech fits the problem that you're trying to solve, as opposed to being locked into adding code on top of some bloated monolith (though that comes with certain tradeoffs and drawbacks, in regards to complexity and maintenance).
I'm personally a fan of Weld since its the reference implementation of the CDI spec.
A friend of mine built an MVC library around Javalin that uses Dagger as the DI container: https://github.com/jehugaleahsa/javalin-mvc
Given the various compelling and (by now, here, I hope) well-understood advantages, it should be the default choice. Certainly for a site expecting hits in the mid-hundreds, total. Maybe ten simultaneous connections the day of?
FWIW, I'm not in the web app world, so my wedding website was a single page HTML; not a single page app, let alone a content management system - I googled something like "Wedding page HTML template", then grabbed a HTML template from w3schools, opened it in notepad, and put my own words and IMG tags. It looked pretty, was "responsive" by default, took very little time to create, and worked solidly.
I'm sufficiently old school / ignorant / pragmatic / lazy / focused / something, to wonder why complicate a wedding page with anything else :-)
(I mean, I'm old and ignorant and simple enough that I keep misinterpreting what "static site generator" is :P )
Maybe the wedding page has forms, needs to store data and author is most familiar with Java or maybe they are getting creative and are calling third party services for convenience, lots of reasons. A simpler solution could be nginx/cgi and a local file for the DB, or cloudfare workers(you can render HTML from them) and their key/value storage.
OP can reply why a database was useful for his wedding site, if he wants; I can think of some reasons why it might be.
Static site generation, and a few other ideas, challenges this basic pattern, but most of the worlds applications use it. Javelin is trying to make the pattern better on several axes, simplicity first-and-foremost, and not replace it.
And (in the Java/enterprise world) is completely correct.
It was overkill and I loved it.
The main drawbacks were people who are not comfortable using the web and people remembering their unique adjective-noun passphrase. Also, I'm not very savvy when it comes to CSS or design so my giddiness was crushed demoing what I thought was the MVP to the "project manager".
I don't want to detract this from being about Javalin though. It's fantastic. I would recommend it and use it again for projects.
You can put the hash in a #xxxx part of the URL so it doesn't show up in logs if thats important to you. The downside is you need to give everyone their own URL (maybe via a QR code on a paper invite). But you already had to give everyone a pass-phrase so whatever :)
This looks really nice! I wish more Java devs approached things with KISS and simple in mind, rather than looking at every problem as a way to apply some obscure design pattern that involves 15 levels of abstraction and indirection for a relatively simple microservice.
Virtual threads in Java 19: https://www.infoq.com/articles/java-virtual-threads/
I have to see I’m pleased to see Java moving fast those days.
I’m still digesting 17 and 18, but that give me a reason to look at 19.
I honestly project loom was far from reaching that level of “standardness” … so I haven’t looked into it in years
Show HN: Javalin 1.0 – A Kotlin/Java web framework - https://news.ycombinator.com/item?id=15644430 - Nov 2017 (87 comments)
Show HN: Javalin, a Java/Kotlin REST API Framework - https://news.ycombinator.com/item?id=14434089 - May 2017 (21 comments)
Java deserves its comeback among the wave of nu-programming we are going through.
What is nu-programming? And why would Java deserve a comeback?
I made that one up.
>And why would Java deserve a comeback?
Java is an extremely mature piece of technology, and having used it on the enterprise, I can attest that few things come close on flexibility and stability. Also, functional languages on top of the JVM (clojure, kotlin, scala) are very interesting on their own. Java deserves some love from this new wave of paradigm changes like "write-once-run-anywhere" v2.0, monadic programming, serverless functions, etc...
Very much agreed, as long as you don't have to deal with legacy projects with Java 8 to Java 11 migration, which might sour the view of the language as a whole for some folks. I think Java doesn't get as much love nowadays due to some of the frameworks having to deal with historical baggage (e.g. Spring being unwieldy, which is at least partially why people prefer Spring Boot), though Java probably isn't the only language for which this is the case: https://earthly.dev/blog/brown-green-language/
That said, Java and .NET are both good options in my eyes - reasonably productive, with pretty decent type systems and good runtime performance. Not perfect, but almost always decent choices. Oh, and the runtimes themselves are pretty great, especially because we get languages like Kotlin, Scala for the JVM and F# for the CLR.
Then again, for different use cases I wouldn't scoff at Python, Ruby or Node either. Even something like PHP can be passable in some cases.
Thank you!
Congratulations on the major release and keep up the good work.
Though I ended up leaning towards KTor, with Javalin as a close second.
How would anyone with more knowledge compare the two (Javalin and KTor)?
I'd love to hear more on this as well. While I've done some minor stuff with Ktor, because JetBrains, I too would love some insight related to these as well.
As far as I know it's inspiration for Spring Boot, and it's simpler and ready to use on production.
Just a minor nitpick (for those who care about the details of OpenJDK's development): virtual threads are not Project Loom, but rather one of the JEPs contributed to the JDK by Project Loom. There are two in JDK 19 (425 and 428), another in advanced stages (429), and there will probably be more. OpenJDK Projects aren't features but teams working on producing features focused on some area and then offering them to the JDK.
Thanks for clearing that up. Would adding a "from" be sufficient?
and it uses Virtual Threads (“Project Loom”) by default
and it uses Virtual Threads (from “Project Loom”) by default
Also spotted the Vue plugin - great to see this as so many light weight web apps with a few pages can benefit from using Vue but setting up all the build chain is such a drama that it's usually not worth it. Look forward to trying it out.
In general thanks for keeping Javalin simple and opinionated, it is my goto when I get to decide!
Not the big data Spark. This one: sparkjava.com
A nice piece of work in its own right.
Spark was fine, but was abandoned. Which means it was stuck using an end-of-life’d version of Jetty. Which meant that we could no longer pass security audits.
Whereas Javalin continues to get frequently updated.
I suppose I'll try Javalin then next time I have the choice.
Aw, that's sad to hear. I've been out of JVM-in-anger space for awhile, but I always liked Spark when I was.
One question about the response methods: If I would be using something like `ctx.result(inputStream)` - would it block the [lightweight] thread until all data is written. Or would the handler return and the data transfer happen in the background?
One use-case I've only see marginally addressed in some frameworks is being able to run code past the point where all data is written - e.g. to record logs and metrics around the outcome of a request - e.g. whether the client hung up or all data was stored in socket buffers. If sending the result "blocks", that kind of code can be added naturally with a try/finally block around it.
As far as I understand the docs, it might not block, but one might be able to install an "After" handler? Or would that also only run before the response is actually sent?
There is no difference between OS threads and virtual threads in this scenario, and everything in Javalin is blocking (by default).
The `ctx.result()` method doesn't actually write anything directly, it sets a result that will be written later. If you add an "After" handler, this also happens before the request is written. When the request is finally written, it's written on the same thread, and it is a blocking operation.
There is a `RequestLogger` you can hook into that fires after the response has been written.
- how do virtual threads fit into a web framework?
- do virtual threads make debugging more difficult? (A more general JVM question, I suppose)
- any performance / readability benefit, or just a coolness factor?
Yes, Javalin is built on top of Jetty.
- how do virtual threads fit into a web framework?
Most JVM web frameworks (Jetty included) have a thread based request lifecyle (one thread handles one request from beginning to end). The java.lang.Threads are extremely expensive (~1mb memory), while Virtual Threads are very cheap (~0mb memory). We also have another ThreadPool for JSON streaming, and one for writing async responses. Using Virtual Threads for these things reduces overall memory pressure.
- do virtual threads make debugging more difficult? (A more general JVM question, I suppose)
Not that I know, but I've never used this in production myself.
- any performance / readability benefit, or just a coolness factor?
The alternative here is java.lang.Threads, so primarily the memory footprint. There is no readability difference. When developing, you don't see this at all.
1. It's built natively on Jetty - very tight integration, not just some libs running in a Jetty container.
2. Web is inherently Request/Response - all of this can be handled with dramatically less resource requirements using Virtual Threads. Web is sort of the absolutely-best-use-case for Virtual Threads where as a Game Engine would be the opposite of that (one critical rendering thread and MAYBE a few extra long-lived threads for processing physics, audio, etc.)
3. I haven't tried debugging a Loom project but it's been in incubation for just under 100 years so I have to imagine this has been figured out.
4. About twice the throughput and 1/2 the latency of full OS threads - https://github.com/ebarlas/project-loom-comparison
public static void main(String[] args) {
var app = Javalin.create(/*config*/)
.get("/", ctx -> ctx.result("Hello World"))
.start(7070);
}for a single file...i can just run java. but it would be nice to see a gradle setup that keeps simplicity and yet can have thousands of files and testcases organised in a nice structure.
secondly, it is nice that you have Jetty hooks. Can you also have CORS & proxy protocol ? its literally mandatory to have this stuff if ur deploying on any of the k8s based clouds.
e.g. https://stackoverflow.com/questions/73225314/accept-proxy-pr...
Edit: looks like its alive and kicking : https://sparkjava.com/news
had no problems at all. i really like it. thanks!
In either case Loom and frameworks that use it are super exciting! I’m looking forward to removing the 20 different thread pools we need to avoid deadlocks and just using the common one in our Clojure apps.
wsHandler := func(w http.ResponseWriter, r \*http.Request) {
conn, err := upgrader.Upgrade(w, r)
if err != nil {
w.SendStatus(http.StatusInternalServerError)
return
}
ctx, ctxCancel := context.WithCancel(context.Background())
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer sync.Done()
for {
msg, err := conn.ReadMessage()
if err != nil {
return
}
}
}()
msgPipe := make(chan []byte, 1024)
wg.Add(1)
go func() {
defer wg.Done()
for {
select {
case <-ctx.Done():
return
case msg := <- msgPipe
if err := conn.WriteMessage(msg); err != nil { return }
}
}
}()
go func() {
<- ctx.Done()
wg.Wait()
close(msgPipe)
}()
}
I like Go, but I found writing WebSocket code very annoying.Have you tried it yet with GraalVM and native compilation?
Any gotchas?
Kudos.
Edit: I was looking at the wrong snippet, the offending snippet has been fixed.
.get("/", ctx -> ctx.result("Hello World"))
Could also be written as .get("/") { ctx -> ctx.result("Hello World") }
The second form seems much more common in Kotlin. .get("/", { ctx -> ctx.result("Hello World") })
Closures defined only by the arrow are a Java thing... or so I thought. :DIn fairness, I think you did copy the Java version. There's a little switch in the code boxes where you can toggle the language, if you switch to Kotlin you get the version with trailing closures :)
Edit: I'm an idiot, I see the snippet you mean now. It's been updated now, give it a minute.
Sinatra, also Ruby, predates Express and is based on Rack (in a similar way to Express, which used to be based on Connect if I’m not mistaken).
I’m not sure what inspired Sinatra, but that’s probably the influence you’re seeing in these frameworks.
We use reflection to build the Virtual Thread Factory if the user has Loom enabled in their enviroment: https://github.com/javalin/javalin/blob/master/javalin/src/m...
Preview language features are different as they require compiling with --enable-preview, which creates a "poisoned" class file that cannot be loaded at all without preview enabled, so preview language features are not recommended for use by libraries, but preview APIs are fine (see JEP 12: Preview Features https://openjdk.org/jeps/12).