Modern Java web framework built on Spark. Convention over configuration — annotation-based routing, dependency injection, database migrations, middleware, and real-time capabilities without the boilerplate.
Full docs at https://obsidian-java.com/docs
@Controller
public class UserController extends BaseController
{
@Inject
private UserRepository userRepository;
@GET("/users")
public String index(Request req, Response res) {
return render("users/index.html", map("users", userRepository.findAll()));
}
@POST("/users")
@CsrfProtect
public String store(Request req, Response res) {
userRepository.create(req.queryParams("name"), req.queryParams("email"));
return redirectWithFlash("/users", "User created.");
}
}public class CreateUsersTable extends Migration
{
@Override
public void up() {
create("users", table -> {
table.id();
table.string("name").notNull();
table.string("email").notNull().unique();
table.timestamps();
});
}
}@LiveComponentImpl
public class Counter extends LiveComponent {
@State
private int count = 0;
@Action
public void increment() { count++; }
@Action
public void decrement() { count--; }
public int getCount() { return count; }
public String template() {
return "components/counter.html";
}
}{{ component('Counter') | raw }}<div live:id="{{ _id }}">
<h2>Count: {{ count }}</h2>
<button live:click="increment">+</button>
<button live:click="decrement">-</button>
<div live:loading>Updating...</div>
</div>Clear structure, no ceremony. Obsidian gives you useful conventions without forcing rigid patterns — use what you need, ignore the rest.
Built on the community-maintained Spark fork with support for Java 11, 17, and 21.