All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.rojoma.simplearm.v2.Resource.scala Maven / Gradle / Ivy

The newest version!
package com.rojoma.simplearm.v2

import java.lang.AutoCloseable
import java.sql.Connection
import scala.concurrent.duration.{Duration, FiniteDuration}
import java.util.concurrent.{ExecutorService, TimeUnit}

trait Resource[A] {
  /** Called after the resource is opened but before the managed value
    * is passed to the code which wants to use it.  If this throws (or
    * otherwise exits abnormally) the resource is NOT closed. */
  def openBeforeTry(a: A) {}
  /** Called after the resource is opened but before the managed value
    * is passed to the code which wants to use it.  If this throws (or
    * otherwise exits abormally) the resource is closed. */
  def openAfterTry(a: A) {}

  /** Closes the resource when the user code exits normally or via
    * `ControlException` (e.g., `return`). */
  def close(a: A)

  /** Closes the resource when the user code exits via any
    * non-`ControlException`. */
  def closeAbnormally(a: A, cause: Throwable) { close(a) }
}

sealed trait LowPriorityImplicits {
  // for legacy classes that have close() but do not implement AutoCloseable
  type ReflectiveCloseable = { def close(): Unit }

  @deprecated(message="Implement a specific instance of Resource for this type", since="2.3.1") // because of https://github.com/scala/bug/issues/11656
  implicit def duckCloseResource[A <: ReflectiveCloseable] = new Resource[A] {
    import scala.language.reflectiveCalls
    def close(r: A) = r.close()
    override def toString = "Resource[{ def close() : Unit }]"
  }
}

/** An instance of this must be implicitly visible in order to manage an ExecutorService. */
abstract class ExecutorShutdownTimeout(val duration: Duration) {
  def onTimeout(executorService: ExecutorService)
}

sealed trait MediumPriorityImplicits extends LowPriorityImplicits {
  private[this] val autoClosableResourceInstance = new Resource[AutoCloseable] {
    def close(r: AutoCloseable) = r.close()
    override def toString = "Resource[java.lang.AutoCloseable]"
  }

  implicit def autoCloseableResource[A <: AutoCloseable]: Resource[A] = autoClosableResourceInstance.asInstanceOf[Resource[A]]

  implicit def executorServiceResource[A <: ExecutorService](implicit executorShutdownTimeout: ExecutorShutdownTimeout) = new Resource[A] {
    def close(r: A) = {
      r.shutdown()
      if(executorShutdownTimeout.duration.isFinite) {
        if(!r.awaitTermination(executorShutdownTimeout.duration.toMillis, TimeUnit.MILLISECONDS)) {
          executorShutdownTimeout.onTimeout(r)
        }
      } else {
        while(!r.awaitTermination(100000000, TimeUnit.MILLISECONDS)) {}
      }
    }
    override def toString = "Resource[java.util.concurrent.ExecutorService]"
  }
}

object Resource extends MediumPriorityImplicits {
  object Noop extends Resource[Any] {
    def close(a: Any) {}
  }

  /** A Resource for a JDBC Connection.
    *
    * It is implementation-defined behavior to close a JDBC connection
    * with an open transaction, and at least one database interprets
    * that as "throw an exception if there is a pending transaction".
    * This rolls back any pending transaction before closing the
    * connection.  If the rollback throws, the close will still be
    * attempted, and if the close also throws its exception will be
    * added to the rollback exception's suppressed list.
    */
  implicit object connectionResource extends Resource[Connection] {
    def close(conn: Connection) {
      try {
        if(!conn.getAutoCommit) conn.rollback()
      } catch {
        case e: Throwable =>
          try {
            conn.close()
          } catch {
            case e2: Throwable =>
              e.addSuppressed(e2)
          }
          throw e
      }
      conn.close()
    }
  }

  def executorShutdownTimeout(duration: FiniteDuration)(onTimeout: ExecutorService => Any): ExecutorShutdownTimeout = {
    val ot = onTimeout
    new ExecutorShutdownTimeout(duration) {
      def onTimeout(executorService: ExecutorService) = ot(executorService)
    }
  }

  val executorShutdownNoTimeout: ExecutorShutdownTimeout = new ExecutorShutdownTimeout(Duration.Inf) {
    def onTimeout(executorService: ExecutorService) {}
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy