-
Notifications
You must be signed in to change notification settings - Fork 29
/
Using.scala
148 lines (134 loc) · 4.93 KB
/
Using.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
143
144
145
146
147
148
package com.thoughtworks.dsl
package keywords
import com.thoughtworks.dsl.Dsl
import com.thoughtworks.dsl.Dsl.{!!, Keyword}
import com.thoughtworks.dsl.keywords.Catch.{CatchDsl, DslCatch}
import com.thoughtworks.dsl.Dsl.TryFinally
import scala.concurrent.{ExecutionContext, Future}
import scala.language.implicitConversions
import scala.util.control.NonFatal
/** This [[Using]] keyword automatically manage resources in [[scala.concurrent.Future]], [[domains.task.Task]],
* and other asynchrounous domains derived from `Future` or `Task`.
*
* @author 杨博 (Yang Bo)
* @see [[dsl]] for usage of this [[Using]] keyword in continuations
*/
final case class Using[R <: AutoCloseable](open: () => R) extends AnyVal with Keyword[Using[R], R]
object Using {
implicit def implicitUsing[R <: AutoCloseable](r: => R): Using[R] = Using[R](r _)
trait ScopeExitHandler extends AutoCloseable
/** Returns a [[Using]] keyword to execute a [[ScopeExitHandler]] when exiting the nearest enclosing scope
* that is annotated as [[Dsl.reset @reset]],
* (or the nearest enclosing function if [[compilerplugins.ResetEverywhere]] is enabled).
*
* @note This method is similar to [[apply]],
* except the parameter type is changed from a generic `R` to the SAM type [[ScopeExitHandler]],
* which allows for function literal expressions
* in Scala 2.12+ or Scala 2.11 with `-Xexperimental` compiler option.
*
* @example The following function will perform `n *= 2` after `n += 20`:
*
* {{{
* import scala.concurrent.Future
* import com.thoughtworks.dsl.keywords.Using.scopeExit
* var n = 1
* def multiplicationAfterAddition = Future {
* !scopeExit { () =>
* n *= 2
* }
* n += 20
* }
* }}}
*
* Therefore, the final value of `n` should be `(1 + 20) * 2 = 42`.
*
* {{{
* multiplicationAfterAddition.map { _ =>
* n should be(42)
* }
* }}}
*
*/
def scopeExit(r: => ScopeExitHandler) = new Using(r _)
def apply[R <: AutoCloseable](r: => R)(
implicit dummyImplicit: DummyImplicit = DummyImplicit.dummyImplicit): Using[R] = new Using(r _)
@deprecated("[[keywords.Catch]] will be removed in favor of [[Dsl.TryCatch]].", "Dsl.scala 1.4.0")
private[Using] def throwableContinuationUsingDsl[Domain, Value, R <: AutoCloseable](
implicit catchDsl: DslCatch[Domain, Domain, Value],
shiftDsl: Dsl[Shift[Domain, Value], Domain, Value]
)
: Dsl[Using[R], Domain !! Value, R] = {
(keyword: Using[R], handler: R => Domain !! Value) => (outerHandler: Value => Domain) =>
val r = keyword.open()
Catch
.tryCatch { value: Value =>
r.close()
outerHandler(value)
}
.apply(Shift(handler(r)).cpsApply(_), {
case NonFatal(e) =>
r.close()
_ {
throw e
}
})
}
implicit def continuationUsingDsl[Domain, Value, R <: AutoCloseable](
implicit tryFinally: TryFinally[Value, Domain, Domain, Domain],
shiftDsl: Dsl[Shift[Domain, Value], Domain, Value]
): Dsl[Using[R], Domain !! Value, R] = { (keyword: Using[R], handler: R => Domain !! Value) =>
_ {
val r = keyword.open()
try {
!Shift(handler(r))
} finally {
r.close()
}
}
}
@deprecated("[[keywords.Catch]] will be removed in favor of [[Dsl.TryCatch]].", "Dsl.scala 1.2.0")
private[Using] def throwableContinuationUsingDsl[Domain, Value, R <: AutoCloseable](
implicit catchDsl: CatchDsl[Domain, Domain, Value],
shiftDsl: Dsl[Shift[Domain, Value], Domain, Value]
): Dsl[Using[R], Domain !! Value, R] = {
throwableContinuationUsingDsl(catchDsl: DslCatch[Domain, Domain, Value],
shiftDsl: Dsl[Shift[Domain, Value], Domain, Value])
}
implicit def scalaFutureUsingDsl[R <: AutoCloseable, A](implicit executionContext: ExecutionContext)
: Dsl[Using[R], Future[A], R] = { (keyword: Using[R], handler: R => Future[A]) =>
Future(keyword.open()).flatMap { r: R =>
def onFailure(e: Throwable): Future[Nothing] = {
try {
r.close()
Future.failed(e)
} catch {
case NonFatal(e2) =>
Future.failed(e2)
}
}
def onSuccess(a: A): Future[A] = {
try {
r.close()
Future.successful(a)
} catch {
case NonFatal(e2) =>
Future.failed(e2)
}
}
def returnableBlock(): Future[A] = {
val fa: Future[A] = try {
handler(r)
} catch {
case NonFatal(e) =>
return onFailure(e)
}
fa.recoverWith {
case NonFatal(e) =>
onFailure(e)
}
.flatMap(onSuccess)
}
returnableBlock()
}
}
}