Java Web Build Examples
This page contains examples of using Mill as a build tool for web-applications. It covers setting up a basic backend server with a variety of server frameworks
Jetty Hello World App
import mill._, javalib._
object hello extends RootModule with JavaModule {
def ivyDeps = Agg(
ivy"org.eclipse.jetty:jetty-server:9.4.43.v20210629",
ivy"javax.servlet:javax.servlet-api:4.0.1"
)
object test extends JavaTests with TestModule.Junit4
}
This example demonstrates how to set up a simple Jetty webserver,
able to handle a single HTTP request at /
and reply with a single response.
> mill test
...HelloJettyTest.testHelloJetty finished...
> mill runBackground
> curl http://localhost:8085
...<h1>Hello, World!</h1>...
> mill clean runBackground
Spring Boot Hello World App
import mill._, javalib._
object hello extends RootModule with JavaModule {
def ivyDeps = Agg(
ivy"org.springframework.boot:spring-boot-starter-web:2.5.6",
ivy"org.springframework.boot:spring-boot-starter-actuator:2.5.6"
)
object test extends JavaTests with TestModule.Junit5 {
def ivyDeps = super.ivyDeps() ++ Agg(
ivy"org.springframework.boot:spring-boot-starter-test:2.5.6"
)
}
}
This example demonstrates how to set up a simple Spring Boot webserver,
able to handle a single HTTP request at /
and reply with a single response.
> mill test
...com.example.HelloSpringBootTest#shouldReturnDefaultMessage() finished...
> mill runBackground
> curl http://localhost:8086
...<h1>Hello, World!</h1>...
> mill clean runBackground
Spring Boot TodoMvc App
import mill._, javalib._
object hello extends RootModule with JavaModule {
def ivyDeps = Agg(
ivy"org.springframework.boot:spring-boot-starter-data-jpa:2.5.4",
ivy"org.springframework.boot:spring-boot-starter-thymeleaf:2.5.4",
ivy"org.springframework.boot:spring-boot-starter-validation:2.5.4",
ivy"org.springframework.boot:spring-boot-starter-web:2.5.4",
ivy"javax.xml.bind:jaxb-api:2.3.1",
ivy"org.webjars:webjars-locator:0.41",
ivy"org.webjars.npm:todomvc-common:1.0.5",
ivy"org.webjars.npm:todomvc-app-css:2.4.1",
)
trait HelloTests extends JavaTests with TestModule.Junit5{
def mainClass = Some("com.example.TodomvcApplication")
def ivyDeps = super.ivyDeps() ++ Agg(
ivy"org.springframework.boot:spring-boot-starter-test:2.5.6"
)
}
object test extends HelloTests{
def ivyDeps = super.ivyDeps() ++ Agg(
ivy"com.h2database:h2:2.3.230",
)
}
object integration extends HelloTests {
def ivyDeps = super.ivyDeps() ++ Agg(
ivy"org.testcontainers:testcontainers:1.18.0",
ivy"org.testcontainers:junit-jupiter:1.18.0",
ivy"org.testcontainers:postgresql:1.18.0",
ivy"org.postgresql:postgresql:42.6.0",
)
}
}
This is a larger example using Spring Boot, implementing the well known TodoMVC example app. Apart from running a webserver, this example also demonstrates:
-
Serving HTML templates using Thymeleaf
-
Serving static Javascript and CSS using Webjars
-
Querying a SQL database using JPA and H2
-
Unit testing using a H2 in-memory database
-
Integration testing using Testcontainers Postgres in Docker
> mill test
...com.example.TodomvcTests#homePageLoads() finished...
...com.example.TodomvcTests#addNewTodoItem() finished...
> mill integration
...com.example.TodomvcIntegrationTests#homePageLoads() finished...
...com.example.TodomvcIntegrationTests#addNewTodoItem() finished...
> mill test.runBackground
> curl http://localhost:8087
...<h1>todos</h1>...
> mill clean runBackground
Micronaut Hello World App
import mill._, javalib._
object hello extends RootModule with MicronautModule {
def micronautVersion = "4.5.3"
def ivyDeps = Agg(
ivy"io.micronaut:micronaut-http-server-netty:$micronautVersion",
ivy"io.micronaut.serde:micronaut-serde-jackson:2.10.1",
ivy"ch.qos.logback:logback-classic:1.5.3",
)
object test extends MavenTests with TestModule.Junit5{
def ivyDeps = super.ivyDeps() ++ Agg(
ivy"io.micronaut:micronaut-http-client:$micronautVersion",
ivy"io.micronaut.test:micronaut-test-junit5:4.4.0",
ivy"org.junit.jupiter:junit-jupiter-api:5.8.1",
ivy"org.junit.jupiter:junit-jupiter-engine:5.8.1"
)
}
}
trait MicronautModule extends MavenModule{
def micronautVersion: String
def processors = T{
defaultResolver().resolveDeps(
Agg(
ivy"io.micronaut.data:micronaut-data-processor:4.7.0",
ivy"io.micronaut:micronaut-http-validation:$micronautVersion",
ivy"io.micronaut.serde:micronaut-serde-processor:2.9.0",
ivy"io.micronaut.validation:micronaut-validation-processor:4.5.0",
ivy"io.micronaut:micronaut-inject-java:$micronautVersion"
)
)
}
def javacOptions = Seq(
"-processorpath",
processors().map(_.path).mkString(":"),
"-parameters",
"-Amicronaut.processing.incremental=true",
"-Amicronaut.processing.group=example.micronaut",
"-Amicronaut.processing.module=todo",
"-Amicronaut.processing.annotations=example.micronaut.*",
)
}
This example demonstrates how to set up a simple Micronaut example service, using the code from the Micronaut Tutorial.
To preserve compatibility with the file layout from the example project, we use MavenModule
,
which follows the src/main/java
and src/test/java
folder convention.
Although Mill does not have a built in MicronautModule
, this example shows how easy
it is to define it yourself as trait MicronautModule
: setting up the annotation processor
classpath as a JavaModule
and setting up the annotation via javacOptions
. Once defined,
you can then use `MicronautModule in your build just like you.
The MicronautModule
shown here does not implement the full functionality of the micronaut
CLI; in particular, support for Micronaut AOT compilation is missing. But it easily can be
extended with more features as necessary.
> mill test
...example.micronaut.HelloControllerTest#testHello()...
> mill runBackground
> curl http://localhost:8087/hello
...Hello World...
> mill clean runBackground
Micronaut TodoMvc App
import mill._, javalib._
object hello extends RootModule with MicronautModule {
def micronautVersion = "4.4.3"
def runIvyDeps = Agg(
ivy"ch.qos.logback:logback-classic:1.5.3",
ivy"com.h2database:h2:2.2.224",
)
def ivyDeps = Agg(
ivy"io.micronaut:micronaut-http-server-netty:$micronautVersion",
ivy"io.micronaut.serde:micronaut-serde-jackson:2.9.0",
ivy"io.micronaut.data:micronaut-data-jdbc:4.7.0",
ivy"io.micronaut.sql:micronaut-jdbc-hikari:5.6.0",
ivy"io.micronaut.validation:micronaut-validation:4.5.0",
ivy"io.micronaut.views:micronaut-views-htmx:5.2.0",
ivy"io.micronaut.views:micronaut-views-thymeleaf:5.2.0",
ivy"org.webjars.npm:todomvc-common:1.0.5",
ivy"org.webjars.npm:todomvc-app-css:2.4.1",
ivy"org.webjars.npm:github-com-bigskysoftware-htmx:1.9.10",
)
object test extends MavenTests with TestModule.Junit5{
def ivyDeps = super.ivyDeps() ++ Agg(
ivy"com.h2database:h2:2.2.224",
ivy"io.micronaut:micronaut-http-client:$micronautVersion",
ivy"io.micronaut.test:micronaut-test-junit5:4.4.0",
ivy"org.junit.jupiter:junit-jupiter-api:5.8.1",
ivy"org.junit.jupiter:junit-jupiter-engine:5.8.1"
)
}
}
trait MicronautModule extends MavenModule{
def micronautVersion: String
def processors = T {
defaultResolver().resolveDeps(
Agg(
ivy"io.micronaut.data:micronaut-data-processor:4.7.0",
ivy"io.micronaut:micronaut-http-validation:$micronautVersion",
ivy"io.micronaut.serde:micronaut-serde-processor:2.9.0",
ivy"io.micronaut.validation:micronaut-validation-processor:4.5.0",
ivy"io.micronaut:micronaut-inject-java:$micronautVersion"
)
)
}
def javacOptions = Seq(
"-processorpath",
processors().map(_.path).mkString(":"),
"-parameters",
"-Amicronaut.processing.incremental=true",
"-Amicronaut.processing.group=example.micronaut",
"-Amicronaut.processing.module=todo",
"-Amicronaut.processing.annotations=example.micronaut.*",
)
}
This example is a more complete example using Micronaut, adapted from
https://github.com/sdelamo/todomvc. On top of the MicronautModule
and
annotation processing demonstrated by the previous example, this example
shows how a "full stack" web application using Micronaut looks like:
-
Thymeleaf for HTML templating
-
Webjars for Javascript and CSS
-
HTMX for interactivity
-
Database interactions using JDBC and H2
-
Controllers, Repositories, Entities, Forms
-
A more detailed test suite
Again, the example MicronautModule
is by no means complete, but it demonstrates
how Mill can be integrated with Micronaut’s annotation processors and configuration,
and can be extended to cover additional functionality in future
> mill test
...example.micronaut.LearnJsonTest...
...example.micronaut.TodoTest...
...example.micronaut.TodoItemMapperTest...
...example.micronaut.TodoItemControllerTest...
...example.micronaut.HtmxWebJarsTest...
> mill runBackground
> curl http://localhost:8088
...<h1>todos</h1>...
> mill clean runBackground