Home

Awesome

Managerial

Maven CI

Managerial is a small, dependency-free library providing Managed, a composable type for setting up and tearing down Resources.

Motivation

This library can aid with basic automatic resource management, that is, automatically closing or tearing down resources once they have been used, regardless of exceptions. In this way, it is similar to Scala's Using, Java's try-with-resources, etc. This is not very exciting, but perhaps useful in some circumstances.

Where Managerial really shines is constructing a program in your main method. Building a program (especially with manual dependency injection) often requires setup and teardown of various resources which may depend on each other. It's also often useful to have side-effects (e.g. logging) interspersed in this setup and teardown. Doing this construction manually is tedious and error-prone; in particular, developers can easily forget to tear down a resource, or may tear down resources in a different order than they were setup. Any exceptions during setup, usage, or teardown further complicate matters.

Managerial makes these errors impossible by allowing for composition of Managed resources in a monadic style (i.e. for comprehensions, flatMap, map).

None of the ideas in the lib are particularly novel (see Related Libraries). But, some may find this particular combination of features enticing.

Installation

Managerial is available on Maven Central for Scala 2.12, 2.13, and 3. There are two artifacts:

Add the following dependency description to your build.sbt:

"ca.dvgi" %% "managerial" % "<latest>"

Usage

Managed[T] instances are created via Managed#apply, Managed#setup, or Managed#from. Additionally, arbitrary actions can be made into Managed[Unit] instances via various Managed#eval methods.

Multiple Managed instances are composed or stacked via flatMap, generally with a for comprehension.

Once the Managed stack is composed, the underlying resources are built and used with use or useUntilShutdown. Setup occurs in the order of the for comprehension or flatMaps, and teardown happens in the reverse order.

Non-fatal exceptions are handled as follows:

For more details, see the Scaladocs.

Basic Automatic Resource Management Example

file.txt will be automatically closed after use, regardless of exceptions thrown.

import ca.dvgi.managerial._
val fileContents = Managed.from(scala.io.Source.fromFile("file.txt")).use(_.mkString)

Composed Resources Example

This is a more full-featured example, showing Managerial's typical use-case.

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")
  }
}

When run, this program outputs:

Starting setup...
Started HealthCheckServer
Started ApiServer
Marked HealthCheckServer Ready
Startup is finished!
^C
Marked HealthCheckServer Unready
Stopped ApiServer
Stopped HealthCheckServer
Finished teardown

Compatibility with Twitter Util Managed

The managerial-twitter-util artifact provides asManagerial for com.twitter.util.Managed, and asTwitterUtil for ca.dvgi.managerial.Managed.

Import ca.dvgi.managerial.twitter.util._ to use these methods.

Related Libraries

Twitter Util's Managed

Managerial is very similar in style to Twitter Util's Managed, and borrows a lot of code from it.

Unlike the Twitter Util library, Managerial:

Scala ARM

Managerial is also quite similar to Scala ARM.

Unlike Scala ARM, Managerial:

Scala Stdlib Using

Unlike Scala's Using, Managerial:

cats-effect Resource

Unlike cats-effect's Resource, Managerial:

Contributing

Contributions in the form of Issues and PRs are welcome.