If you:
- like the style of Twitter Util
Managed
(resource composition via monads), - don’t want the Twitter Util dependencies (or any dependencies at all),
- do want a nicer API,
you may enjoy GitHub - dvgica/managerial: A zero-dependency Scala library for managing resources monadically, published for Scala 2.12, 2.13, and 3.0. The lib is likely closest to Scala ARM, but smaller in scope and published for recent versions of Scala.
Let me know what you think!
Full example:
import ca.dvgi.managerial._
object Main extends App {
val server = for {
// create a Managed[Unit] for side effects
_ <- Managed.eval(println("Starting setup..."))(println("Finished teardown"))
// create Managed[Settings] that doesn't require teardown
settings <- Managed.setup(Settings(8080, 7070))
// create Managed[HealthCheckServer], which requires teardown
healthCheckServer <- Managed(new HealthCheckServer(settings))(_.stop())
// Managed#from expects a type class instance for Teardown[T], instead of having teardown specified explicitly.
// ca.dvgi.managerial provides Teardown[AutoCloseable].
_ <- Managed.from(new ApiServer(settings))
// once the ApiServer is started, the HealthCheckServer can show it's ready
_ <- Managed.eval(healthCheckServer.markReady())(healthCheckServer.markUnready())
// evalSetup and evalTeardown allow for side-effects during only setup or only teardown
_ <- Managed.evalSetup(println("Startup is finished!"))
} yield ()
// builds the Managed stack and registers a JVM shutdown hook to do automatic teardown
server.useUntilShutdown()
}
case class Settings(healthCheckPort: Int, apiPort: Int)
class HealthCheckServer(settings: Settings) {
println("Started HealthCheckServer")
def stop(): Unit = {
println("Stopped HealthCheckServer")
}
def markReady(): Unit = println("Marked HealthCheckServer Ready")
def markUnready(): Unit = println("Marked HealthCheckServer Unready")
}
class ApiServer(settings: Settings) extends AutoCloseable {
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration.Duration
import scala.concurrent.Await
import scala.concurrent.Future
val fakeServer = new Thread {
override def run: Unit = {
Await.ready(Future.never, Duration.Inf)
}
}
fakeServer.start()
println("Started ApiServer")
def close(): Unit = {
println("Stopped ApiServer")
}
}