-
Notifications
You must be signed in to change notification settings - Fork 51
/
Copy pathAbstractManagedResource.scala
142 lines (121 loc) · 5.7 KB
/
AbstractManagedResource.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// -----------------------------------------------------------------------------
//
// scala.arm - The Scala Incubator Project
// Copyright (c) 2009 The Scala Incubator Project. All rights reserved.
//
// The primary distribution site is http://jsuereth.github.com/scala-arm
//
// This software is released under the terms of the Revised BSD License.
// There is NO WARRANTY. See the file LICENSE for the full text.
//
// -----------------------------------------------------------------------------
package resource
import _root_.scala.util.control.{ ControlThrowable, Exception }
/**
* An implementation of an ExtractableManagedResource that defers all processing until the user pulls out information using
* either or opt functions.
*/
private[resource] class DeferredExtractableManagedResource[+A,R](val resource: ManagedResource[R], val translate: R => A)
extends ExtractableManagedResource[A] with ManagedResourceOperations[A] { self =>
override def acquireFor[B](f: A => B) : ExtractedEither[List[Throwable], B] = resource acquireFor (translate andThen f)
override def either = ExtractedEither(resource acquireFor translate)
override def opt = Compat.toRightOption(either.either)
override def tried = scala.util.Try(resource apply translate)
override def equals(that: Any) = that match {
case x : DeferredExtractableManagedResource[A,R] => (x.resource == resource) && (x.translate == translate)
case _ => false
}
override def hashCode(): Int = (resource.hashCode << 7) + translate.hashCode + 13
override def toString = "DeferredExtractableManagedResource(" + resource + ", " + translate + ")"
}
/**
* Abstract class implementing most of the managed resource features in terms of an open and close method. This
* is a refinement over ManagedResourceOperations as it defines the acquireForMethod generically using the
* scala.util.control.Exception API.
*/
abstract class AbstractManagedResource[R] extends ManagedResource[R] with ManagedResourceOperations[R] {
/**
* Opens a given resource, returning a handle to execute against during the "session" of the resource being open.
*/
protected def open: R
/**
* Closes a resource using the handle. This method will throw any exceptions normally occurring during the closing of
* a resource.
*/
protected def unsafeClose(handle: R, errors: Option[Throwable]): Unit
/** These are a list of exceptions we *have* to rethrow, regardless of
* a users desires to ensure that thread/return behavior in scala is accurate.
*/
protected def isRethrown(t: Throwable): Boolean = t match {
case _: ControlThrowable => true
case _: InterruptedException => true
case _ => false
}
/** This checks to see if an exception should not be caught, under any circumstances.
* These usually denote fatal program flaws.
*/
protected def isFatal(t: Throwable): Boolean = t match {
case _: java.lang.VirtualMachineError => true
// TODO - Others?
case _ => false
}
/** A catcher of exceptions that will ignore those we consider fatal. */
private final val catchingNonFatal: Exception.Catch[Nothing] =
(new Exception.Catch(Exception.mkThrowableCatcher(e => !isFatal(e), throw _), None, _ => false)
withDesc "<non-fatal>")
override def acquireFor[B](f : R => B) : ExtractedEither[List[Throwable], B] = {
val handle = open
val result = catchingNonFatal either (f(handle))
val close = catchingNonFatal either unsafeClose(handle, result.left.toOption)
// Here we pattern match to make sure we get all the errors.
val either = (result, close) match {
case (Left(t1), _ ) if isRethrown(t1) => throw t1
case (Left(t1), Left(t2)) => Left(t1 :: t2 :: Nil)
case (Left(t1), _ ) => Left(t1 :: Nil)
case (Right(ExtractedEither(Left(ts))), Left(t2)) => Left(ts.asInstanceOf[List[Throwable]] :+ t2)
case (_, Left(t2)) => Left(t2 :: Nil)
case (Right(r), _ ) => Right(r)
}
ExtractedEither(either)
}
}
/**
* This is the default implementation of a ManagedResource that makes use of the Resource type trait.
*/
final class DefaultManagedResource[R : Resource : OptManifest](r : => R) extends AbstractManagedResource[R] { self =>
/** Stable reference to the Resource type trait.*/
protected val typeTrait = implicitly[Resource[R]]
override protected def open: R = {
val resource = r
typeTrait.open(resource)
resource
}
override protected def unsafeClose(r: R, error: Option[Throwable]): Unit =
error match {
case None => typeTrait.close(r)
case Some(t) => typeTrait.closeAfterException(r, t)
}
override protected def isFatal(t: Throwable): Boolean =
typeTrait isFatalException t
override protected def isRethrown(t: Throwable): Boolean =
typeTrait isRethrownException t
/* You cannot serialize resource and send them, so referential equality should be sufficient. */
override def hashCode(): Int = (typeTrait.hashCode << 7) + super.hashCode + 13
// That's right, we use manifest solely for nicer toStrings!
override def toString = "Default[" + implicitly[OptManifest[R]] + " : " + typeTrait + "](...)"
}
/**
* ConstantManagedResource encapsulates a constant value with no associated resource management.
*/
final class ConstantManagedResource[V](value: V) extends AbstractManagedResource[V] {
/**
* Simply return the value given at construction.
*/
override protected def open: V = {
value
}
/**
* Nothing needs to be done to close.
*/
override protected def unsafeClose(handle: V, errors: Option[Throwable]): Unit = {}
}