Skip to content

Resource Management

Safely acquire and release resources with automatic cleanup.

Basic Usage

scala
import wvlet.uni.control.Resource

Resource.withResource(openFile("data.txt")) { file =>
  processFile(file)
} // File is automatically closed

Multiple Resources

scala
Resource.withResources(
  openDatabase(),
  openFile("config.txt")
) { (db, file) =>
  processWithResources(db, file)
} // Both resources are closed in reverse order

AutoCloseable Support

Works with any AutoCloseable:

scala
import java.io.{BufferedReader, FileReader}

Resource.withResource(BufferedReader(FileReader("data.txt"))) { reader =>
  reader.lines().forEach(println)
}

Nested Resources

scala
Resource.withResource(openConnection()) { conn =>
  Resource.withResource(conn.createStatement()) { stmt =>
    Resource.withResource(stmt.executeQuery(sql)) { rs =>
      processResultSet(rs)
    }
  }
}

Error Handling

Resources are closed even if an exception occurs:

scala
try
  Resource.withResource(openFile("data.txt")) { file =>
    if someCondition then
      throw RuntimeException("Processing failed")
    processFile(file)
  }
catch
  case e: RuntimeException =>
    // Handle error, file is already closed
    logger.error("Error", e)

Custom Resource Types

Implement AutoCloseable for custom types:

scala
class DatabasePool extends AutoCloseable:
  private val connections = scala.collection.mutable.ListBuffer[Connection]()

  def getConnection(): Connection = ???

  def close(): Unit =
    // Release all connections
    connections.foreach(_.close())

Resource.withResource(DatabasePool()) { pool =>
  val conn = pool.getConnection()
  // Use connection
}

Resource in Rx

Combine with reactive streams:

scala
import wvlet.uni.rx.Rx

def readLines(path: String): Rx[String] =
  Rx.single {
    Resource.withResource(scala.io.Source.fromFile(path)) { source =>
      source.getLines().toList
    }
  }.flatMap(lines => Rx.fromSeq(lines))

Loan Pattern

The withResource method implements the loan pattern:

scala
def withDatabase[T](f: Database => T): T =
  Resource.withResource(Database.connect()) { db =>
    f(db)
  }

// Usage
val users = withDatabase { db =>
  db.query("SELECT * FROM users")
}

Resource with Design

Integrate with Design for lifecycle management:

scala
import wvlet.uni.design.Design

val design = Design.newDesign
  .bindSingleton[DatabasePool]
  .onStart(_.initialize())
  .onShutdown(_.close())

design.withSession { session =>
  val pool = session.build[DatabasePool]
  // Pool is initialized and will be closed
}

Best Practices

  1. Always use withResource for closeable resources
  2. Handle exceptions appropriately
  3. Close in reverse order for nested resources
  4. Prefer Design lifecycle for application-scoped resources
  5. Log cleanup failures but don't mask original errors

Common Resources

ResourceUsage
FilesBufferedReader, BufferedWriter
DatabaseConnection, Statement, ResultSet
NetworkSocket, HttpClient
StreamsInputStream, OutputStream

Released under the Apache 2.0 License.