/
Play.scala
203 lines (167 loc) · 6.99 KB
/
Play.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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
/*
* Copyright (C) 2009-2019 Lightbend Inc. <https://www.lightbend.com>
*/
package play.api
import java.util.concurrent.atomic.AtomicReference
import akka.Done
import akka.actor.CoordinatedShutdown
import akka.stream.Materializer
import play.api.i18n.MessagesApi
import play.utils.Threads
import scala.concurrent.Await
import scala.concurrent.Future
import scala.concurrent.duration.Duration
import scala.util.control.NonFatal
import javax.xml.parsers.SAXParserFactory
import play.libs.XML.Constants
import javax.xml.XMLConstants
import scala.util.Failure
import scala.util.Success
import scala.util.Try
/**
* Application mode, either `Dev`, `Test`, or `Prod`.
*
* @see [[play.Mode]]
*/
sealed abstract class Mode(val asJava: play.Mode)
object Mode {
@deprecated("Use play.api.Mode instead of play.api.Mode.Mode", "2.6.0")
type Mode = play.api.Mode
@deprecated("Use play.api.Mode instead of play.api.Mode.Value", "2.6.0")
type Value = play.api.Mode
case object Dev extends play.api.Mode(play.Mode.DEV)
case object Test extends play.api.Mode(play.Mode.TEST)
case object Prod extends play.api.Mode(play.Mode.PROD)
lazy val values: Set[play.api.Mode] = Set(Dev, Test, Prod)
}
/**
* High-level API to access Play global features.
*/
object Play {
private val logger = Logger(Play.getClass)
private[play] val GlobalAppConfigKey = "play.allowGlobalApplication"
private[play] val xercesSaxParserFactory = SAXParserFactory.newInstance()
xercesSaxParserFactory.setFeature(Constants.SAX_FEATURE_PREFIX + Constants.EXTERNAL_GENERAL_ENTITIES_FEATURE, false)
xercesSaxParserFactory.setFeature(Constants.SAX_FEATURE_PREFIX + Constants.EXTERNAL_PARAMETER_ENTITIES_FEATURE, false)
xercesSaxParserFactory.setFeature(Constants.XERCES_FEATURE_PREFIX + Constants.DISALLOW_DOCTYPE_DECL_FEATURE, true)
xercesSaxParserFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true)
/*
* A parser to be used that is configured to ensure that no schemas are loaded.
*/
private[play] def XML = scala.xml.XML.withSAXParser(xercesSaxParserFactory.newSAXParser())
/**
* Returns the currently running application, or `null` if not defined.
*/
@deprecated("This is a static reference to application, use DI", "2.5.0")
def unsafeApplication: Application = privateMaybeApplication.get
/**
* Optionally returns the current running application.
*/
@deprecated("This is a static reference to application, use DI instead", "2.5.0")
def maybeApplication: Option[Application] = privateMaybeApplication.toOption
private[play] def privateMaybeApplication: Try[Application] = {
if (_currentApp.get != null) {
Success(_currentApp.get)
} else {
Failure(
sys.error(
s"""
|The global application reference is disabled. Play's global state is deprecated and will
|be removed in a future release. You should use dependency injection instead. To enable
|the global application anyway, set $GlobalAppConfigKey = true.
""".stripMargin
)
)
}
}
/* Used by the routes compiler to resolve an application for the injector. Treat as private. */
def routesCompilerMaybeApplication: Option[Application] = privateMaybeApplication.toOption
/**
* Implicitly import the current running application in the context.
*
* Note that by relying on this, your code will only work properly in
* the context of a running application.
*/
@deprecated("This is a static reference to application, use DI instead", "2.5.0")
implicit def current: Application = privateMaybeApplication.getOrElse(sys.error("There is no started application"))
// _currentApp is an AtomicReference so that `start()` can invoke `stop()`
// without causing a deadlock. That potential deadlock (and this derived complexity)
// was introduced when using CoordinatedShutdown because `unsetGlobalApp(app)`
// may run from a different thread.
private val _currentApp: AtomicReference[Application] = new AtomicReference[Application]()
/**
* Sets the global application instance.
*
* If another app was previously started using this API and the global application is enabled, Play.stop will be
* called on the existing application.
*
* @param app the application to start
*/
def start(app: Application): Unit = synchronized {
val globalApp = app.globalApplicationEnabled
// Stop the current app if the new app needs to replace the current app instance
if (globalApp && _currentApp.get != null) {
logger.info("Stopping current application")
stop(_currentApp.get())
}
app.mode match {
case Mode.Test =>
case mode =>
logger.info(s"Application started ($mode)${if (!globalApp) " (no global state)" else ""}")
}
// Set the current app if the global application is enabled
// Also set it if the current app is null, in order to display more useful errors if we try to use the app
if (globalApp) {
logger.warn(s"""
|You are using the deprecated global state to set and access the current running application. If you
|need an instance of Application, set $GlobalAppConfigKey = false and use Dependency Injection instead.
""".stripMargin)
_currentApp.set(app)
// It's possible to stop the Application using Coordinated Shutdown, when that happens the Application
// should no longer be considered the current App
app.coordinatedShutdown.addTask(CoordinatedShutdown.PhaseBeforeActorSystemTerminate, "unregister-global-app") {
() =>
unsetGlobalApp(app)
Future.successful(Done)
}
}
}
/**
* Stops the given application.
*/
def stop(app: Application): Unit = {
if (app != null) {
Threads.withContextClassLoader(app.classloader) {
try {
Await.ready(app.stop(), Duration.Inf)
} catch { case NonFatal(e) => logger.warn("Error stopping application", e) }
}
}
}
private def unsetGlobalApp(app: Application) = {
// Don't bother un-setting the current app unless it's our app
_currentApp.compareAndSet(app, null)
}
/**
* Returns the name of the cookie that can be used to permanently set the user's language.
*/
@deprecated("Use the MessagesApi itself", "2.7.0")
def langCookieName(implicit messagesApi: MessagesApi): String =
messagesApi.langCookieName
/**
* Returns whether the language cookie should have the secure flag set.
*/
@deprecated("Use the MessagesApi itself", "2.7.0")
def langCookieSecure(implicit messagesApi: MessagesApi): Boolean =
messagesApi.langCookieSecure
/**
* Returns whether the language cookie should have the HTTP only flag set.
*/
@deprecated("Use the MessagesApi itself", "2.7.0")
def langCookieHttpOnly(implicit messagesApi: MessagesApi): Boolean =
messagesApi.langCookieHttpOnly
/**
* A convenient function for getting an implicit materializer from the current application
*/
implicit def materializer(implicit app: Application): Materializer = app.materializer
}