Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: mrspeaker/Play20
base: 2a8eb3b2b5
...
head fork: mrspeaker/Play20
compare: a8e7d89276
Checking mergeability… Don't worry, you can still create the pull request.
  • 7 commits
  • 18 files changed
  • 0 commit comments
  • 3 contributors
Showing with 127 additions and 48 deletions.
  1. +1 −1  documentation/manual
  2. +1 −1  framework/src/play-test/src/main/scala/play/api/test/Helpers.scala
  3. +1 −1  framework/src/play/src/main/scala/play/api/Application.scala
  4. +4 −4 framework/src/play/src/main/scala/play/api/Configuration.scala
  5. +4 −4 framework/src/play/src/main/scala/play/api/db/DB.scala
  6. +10 −10 framework/src/play/src/main/scala/play/api/db/evolutions/Evolutions.scala
  7. +1 −1  framework/src/play/src/main/scala/play/api/http/ContentTypeOf.scala
  8. +1 −1  framework/src/play/src/main/scala/play/api/http/Writeable.scala
  9. +40 −0 framework/src/play/src/main/scala/play/api/libs/EventSource.scala
  10. +1 −1  framework/src/play/src/main/scala/play/api/libs/concurrent/Akka.scala
  11. +1 −1  framework/src/play/src/main/scala/play/api/libs/concurrent/Promise.scala
  12. +7 −0 framework/src/play/src/main/scala/play/api/mvc/Results.scala
  13. +14 −12 framework/src/play/src/main/scala/play/core/server/Server.scala
  14. +2 −2 framework/src/play/src/main/scala/play/core/system/ApplicationProvider.scala
  15. +23 −6 framework/src/play/src/main/scala/play/core/system/Invoker.scala
  16. +2 −1  framework/src/sbt-plugin/src/main/scala/PlayCommands.scala
  17. +13 −1 framework/src/sbt-plugin/src/main/scala/PlayReloader.scala
  18. +1 −1  framework/test/integrationtest/app/controllers/Application.scala
2  documentation/manual
@@ -1 +1 @@
-Subproject commit 1a8057402954bddb0dc75d415d86a87ce0313c54
+Subproject commit 023de8e0a9cc73ac7728a39a5749cad819f8af4e
View
2  framework/src/play-test/src/main/scala/play/api/test/Helpers.scala
@@ -68,7 +68,7 @@ object Helpers extends Status with HeaderNames {
/**
* Apply pending evolutions for the given DB.
*/
- def evolutionFor(dbName: String): Unit = play.api.db.evolutions.OfflineEvolutions.applyScript(this.getClass.getClassLoader, dbName)
+ def evolutionFor(dbName: String, path: java.io.File = new java.io.File(".")): Unit = play.api.db.evolutions.OfflineEvolutions.applyScript(path, this.getClass.getClassLoader, dbName)
/**
* Extracts the Content-Type of this Content value.
View
2  framework/src/play/src/main/scala/play/api/Application.scala
@@ -33,7 +33,7 @@ import annotation.implicitNotFound
class Application(val path: File, val classloader: ClassLoader, val sources: Option[SourceMapper], val mode: Mode.Mode) {
private val initialConfiguration = Threads.withContextClassLoader(classloader) {
- Configuration.load(mode)
+ Configuration.load(path, mode)
}
// -- Global stuff
View
8 framework/src/play/src/main/scala/play/api/Configuration.scala
@@ -23,9 +23,9 @@ object Configuration {
* loads `Configuration` from 'conf/application.conf' in Dev mode
* @return configuration to be used
*/
- private[play] def loadDev = {
+ private[play] def loadDev(appPath: File) = {
try {
- val file = Option(System.getProperty("config.file")).map(f => new File(f)).getOrElse(new File("conf/application.conf"))
+ val file = Option(System.getProperty("config.file")).map(f => new File(f)).getOrElse(new File(appPath, "conf/application.conf"))
ConfigFactory.load(ConfigFactory.parseFileAnySyntax(file))
} catch {
case e: ConfigException => throw configError(e.origin, e.getMessage, Some(e))
@@ -44,10 +44,10 @@ object Configuration {
* @param mode Application mode.
* @return a `Configuration` instance
*/
- def load(mode: Mode.Mode = Mode.Dev) = {
+ def load(appPath: File, mode: Mode.Mode = Mode.Dev) = {
try {
val currentMode = Play.maybeApplication.map(_.mode).getOrElse(mode)
- if (currentMode == Mode.Prod) Configuration(ConfigFactory.load()) else Configuration(loadDev)
+ if (currentMode == Mode.Prod) Configuration(ConfigFactory.load()) else Configuration(loadDev(appPath))
} catch {
case e: ConfigException => throw configError(e.origin, e.getMessage, Some(e))
case e => throw e
View
8 framework/src/play/src/main/scala/play/api/db/DB.scala
@@ -263,7 +263,7 @@ class BoneCPPlugin(app: Application) extends DBPlugin {
private[db] class BoneCPApi(configuration: Configuration, classloader: ClassLoader) extends DBApi {
- private def error(m: String = "") = throw new Exception("Invalid DB configuration " + m)
+ private def error(db: String, message: String = "") = throw configuration.reportError(db, message)
private val dbNames = configuration.subKeys
@@ -375,9 +375,9 @@ private[db] class BoneCPApi(configuration: Configuration, classloader: ClassLoad
}
val datasources: List[Tuple2[DataSource, String]] = dbNames.map { dbName =>
- val url = configuration.getString(dbName + ".url").getOrElse(error("- could not determine url for " + dbName + ".url"))
- val driver = configuration.getString(dbName + ".driver").getOrElse(error("- could not determine driver for " + dbName + ".driver"))
- val extraConfig = configuration.getConfig(dbName).getOrElse(error("- could not find extra configs"))
+ val url = configuration.getString(dbName + ".url").getOrElse(error(dbName, "Missing configuration [db." + dbName + ".url]"))
+ val driver = configuration.getString(dbName + ".driver").getOrElse(error(dbName, "Missing configuration [db." + dbName + ".driver]"))
+ val extraConfig = configuration.getConfig(dbName).getOrElse(error(dbName, "Missing configuration [db." + dbName + "]"))
register(driver, extraConfig)
createDataSource(dbName, url, driver, extraConfig) -> dbName
}.toList
View
20 framework/src/play/src/main/scala/play/api/db/evolutions/Evolutions.scala
@@ -279,8 +279,8 @@ object Evolutions {
* @param applicationPath the application path
* @param db the database name
*/
- def evolutionScript(api: DBApi, applicationClassloader: ClassLoader, db: String): Seq[Product with Serializable with Script] = {
- val application = applicationEvolutions(applicationClassloader, db)
+ def evolutionScript(api: DBApi, path: File, applicationClassloader: ClassLoader, db: String): Seq[Product with Serializable with Script] = {
+ val application = applicationEvolutions(path, applicationClassloader, db)
val database = databaseEvolutions(api, db)
val (nonConflictingDowns, dRest) = database.span(e => !application.headOption.exists(e.revision <= _.revision))
@@ -334,7 +334,7 @@ object Evolutions {
*
* @param db the database name
*/
- def applicationEvolutions(applicationClassloader: ClassLoader, db: String): Seq[Evolution] = {
+ def applicationEvolutions(path: File, applicationClassloader: ClassLoader, db: String): Seq[Evolution] = {
val upsMarker = """^#.*!Ups.*$""".r
val downsMarker = """^#.*!Downs.*$""".r
@@ -356,7 +356,7 @@ object Evolutions {
}
Collections.unfoldLeft(1) { revision =>
- Option(new File("conf/evolutions/" + db + "/" + revision + ".sql")).filter(_.exists).map(new FileInputStream(_)).orElse {
+ Option(new File(path, "conf/evolutions/" + db + "/" + revision + ".sql")).filter(_.exists).map(new FileInputStream(_)).orElse {
Option(applicationClassloader.getResourceAsStream("evolutions/" + db + "/" + revision + ".sql"))
}.map { stream =>
(revision + 1, (revision, stream.asInput.slurpString))
@@ -409,7 +409,7 @@ class EvolutionsPlugin(app: Application) extends Plugin {
val api = app.plugin[DBPlugin].map(_.api).getOrElse(throw new Exception("there should be a database plugin registered at this point but looks like it's not available, so evolution won't work. Please make sure you register a db plugin properly"))
api.datasources.foreach {
case (ds, db) => {
- val script = evolutionScript(api, app.classloader, db)
+ val script = evolutionScript(api, app.path, app.classloader, db)
if (!script.isEmpty) {
app.mode match {
case Mode.Test => Evolutions.applyScript(api, db, script)
@@ -441,15 +441,15 @@ object OfflineEvolutions {
* @param classloader the classloader used to load the driver
* @param dbName the database name
*/
- def applyScript(classloader: ClassLoader, dbName: String) {
+ def applyScript(appPath: File, classloader: ClassLoader, dbName: String) {
import play.api._
- val c = Configuration.load().getConfig("db").get
+ val c = Configuration.load(appPath).getConfig("db").get
val dbApi = new BoneCPApi(c, classloader)
- val script = Evolutions.evolutionScript(dbApi, classloader, dbName)
+ val script = Evolutions.evolutionScript(dbApi, appPath, classloader, dbName)
if (!Play.maybeApplication.exists(_.mode == Mode.Test)) {
Logger("play").warn("Applying evolution script for database '" + dbName + "':\n\n" + Evolutions.toHumanReadableScript(script))
@@ -464,11 +464,11 @@ object OfflineEvolutions {
* @param classloader the classloader used to load the driver
* @param dbName the database name
*/
- def resolve(classloader: ClassLoader, dbName: String, revision: Int) {
+ def resolve(appPath: File, classloader: ClassLoader, dbName: String, revision: Int) {
import play.api._
- val c = Configuration.load().getConfig("db").get
+ val c = Configuration.load(appPath).getConfig("db").get
val dbApi = new BoneCPApi(c, classloader)
View
2  framework/src/play/src/main/scala/play/api/http/ContentTypeOf.scala
@@ -18,7 +18,7 @@ import scala.annotation._
@implicitNotFound(
"Cannot guess the content type to use for ${A}. Try to define a ContentTypeOf[${A}]"
)
-case class ContentTypeOf[A](mimeType: Option[String])
+case class ContentTypeOf[-A](mimeType: Option[String])
/**
* Default Content-Type typeclasses.
View
2  framework/src/play/src/main/scala/play/api/http/Writeable.scala
@@ -17,7 +17,7 @@ import scala.annotation._
@implicitNotFound(
"Cannot write an instance of ${A} to HTTP response. Try to define a Writeable[${A}]"
)
-case class Writeable[A](transform: (A => Array[Byte]))
+case class Writeable[-A](transform: (A => Array[Byte]))
/**
* Helper utilities for `Writeable`.
View
40 framework/src/play/src/main/scala/play/api/libs/EventSource.scala
@@ -0,0 +1,40 @@
+package play.api.libs
+
+import play.api.mvc._
+import play.api.libs.iteratee._
+import play.api.templates._
+
+import org.apache.commons.lang.{ StringEscapeUtils }
+
+object EventSource {
+
+ case class EventNameExtractor[E](eventName: E => Option[String])
+
+ case class EventIdExtractor[E](eventId: E => Option[String])
+
+ trait LowPriorityEventNameExtractor {
+
+ implicit def non[E]:EventNameExtractor[E] = EventNameExtractor[E](_ => None)
+
+ }
+
+ trait LowPriorityEventIdExtractor {
+
+ implicit def non[E]:EventIdExtractor[E] = EventIdExtractor[E](_ => None)
+
+ }
+
+ object EventNameExtractor extends LowPriorityEventNameExtractor {
+
+ implicit def pair[E]:EventNameExtractor[(String,E)] = EventNameExtractor[(String,E)](p => Some(p._1))
+
+ }
+
+ object EventIdExtractor extends LowPriorityEventIdExtractor
+
+ def apply[E]()(implicit encoder: Comet.CometMessage[E], eventNameExtractor: EventNameExtractor[E], eventIdExtractor:EventIdExtractor[E]) = Enumeratee.map[E] { chunk =>
+ eventNameExtractor.eventName(chunk).map("event: "+ _ + "\n").getOrElse("") +
+ "data: "+encoder.toJavascriptMessage(chunk)+"\r\n\r\n"
+ }
+
+}
View
2  framework/src/play/src/main/scala/play/api/libs/concurrent/Akka.scala
@@ -110,7 +110,7 @@ class AkkaPlugin(app: Application) extends Plugin {
lazy val applicationSystem: ActorSystem = {
applicationSystemEnabled = true
- val system = ActorSystem("application", Configuration.load(app.mode).underlying)
+ val system = ActorSystem("application", Configuration.load(app.path, app.mode).underlying)
Logger("play").info("Starting application default Akka system.")
system
}
View
2  framework/src/play/src/main/scala/play/api/libs/concurrent/Promise.scala
@@ -89,7 +89,7 @@ trait Promise[+A] {
}
-trait Redeemable[A] {
+trait Redeemable[-A] {
def redeem(a: => A): Unit
def throwing(t: Throwable): Unit
}
View
7 framework/src/play/src/main/scala/play/api/mvc/Results.scala
@@ -349,6 +349,13 @@ trait Results {
iteratee => content |>> iteratee)
}
+
+ def feed[C](content: Enumerator[C])(implicit writeable: Writeable[C], contentTypeOf: ContentTypeOf[C]): SimpleResult[C] = {
+ SimpleResult(
+ header = ResponseHeader(status, Map( CONTENT_LENGTH -> "-1")),
+ body = content)
+ }
+
/**
* Set the result's content as chunked.
*
View
26 framework/src/play/src/main/scala/play/core/server/Server.scala
@@ -13,6 +13,7 @@ import akka.actor.Actor._
import akka.routing._
import akka.config._
import akka.pattern.Patterns.ask
+import akka.util.duration._
import akka.util.Timeout
trait WebSocketable {
@@ -24,9 +25,7 @@ trait WebSocketable {
* provides generic server behaviour for Play applications
*/
trait Server {
-
- def mode: Mode.Mode
-
+
// First delete the default log file for a fresh start
try {
scalax.file.Path(new java.io.File(applicationProvider.path, "logs/application.log")).delete()
@@ -38,6 +37,18 @@ trait Server {
Logger.configure(
Map("application.home" -> applicationProvider.path.getAbsolutePath),
mode = mode)
+
+ // Start the main Invoker
+ Invoker.init(applicationProvider)
+
+ // Get a reference on the Action invoker
+ val invoker = Invoker.actionInvoker
+
+ val bodyParserTimeout = {
+ Configuration(Invoker.system.settings.config).getMilliseconds("akka.actor.retrieveBodyParserTimeout").map(_ milliseconds).getOrElse(1 second)
+ }
+
+ def mode: Mode.Mode
def response(webSocketableRequest: WebSocketable)(otheResults: PartialFunction[Result, Unit]) = new Response {
@@ -56,8 +67,6 @@ trait Server {
def handle(result: Result) = (asyncResult orElse websocketErrorResult orElse otheResults)(result)
}
- val invoker = Invoker.actionInvoker
-
def getHandlerFor(request: RequestHeader): Either[Result, (Handler, Application)] = {
import scala.util.control.Exception
@@ -103,13 +112,6 @@ trait Server {
invoker ! Invoker.HandleAction(request, response, action, app)
}
- val bodyParserTimeout = {
- import akka.util.duration._
- Configuration(Invoker.system.settings.config).getMilliseconds("akka.actor.retrieveBodyParserTimeout").map(_ milliseconds).getOrElse(1 second)
- }
-
- import play.api.libs.concurrent._
-
def getBodyParser[A](requestHeaders: RequestHeader, bodyFunction: BodyParser[A]): Promise[Iteratee[Array[Byte], Either[Result, A]]] = {
val future = ask(invoker, Invoker.GetBodyParser(requestHeaders, bodyFunction),bodyParserTimeout)
future.asPromise.map(_.asInstanceOf[Iteratee[Array[Byte], Either[Result, A]]])
View
4 framework/src/play/src/main/scala/play/core/system/ApplicationProvider.scala
@@ -172,7 +172,7 @@ class ReloadableApplication(sbtLink: SBTLink) extends ApplicationProvider {
import play.api.db.evolutions._
Some {
- OfflineEvolutions.applyScript(Play.current.classloader, db)
+ OfflineEvolutions.applyScript(path, Play.current.classloader, db)
sbtLink.forceReload()
Redirect(request.queryString.get("redirect").filterNot(_.isEmpty).map(_(0)).getOrElse("/"))
}
@@ -184,7 +184,7 @@ class ReloadableApplication(sbtLink: SBTLink) extends ApplicationProvider {
import play.api.db.evolutions._
Some {
- OfflineEvolutions.resolve(Play.current.classloader, db, rev.toInt)
+ OfflineEvolutions.resolve(path, Play.current.classloader, db, rev.toInt)
sbtLink.forceReload()
Redirect(request.queryString.get("redirect").filterNot(_.isEmpty).map(_(0)).getOrElse("/"))
}
View
29 framework/src/play/src/main/scala/play/core/system/Invoker.scala
@@ -21,18 +21,35 @@ object Invoker {
case class GetBodyParser(request: RequestHeader, bodyParser: BodyParser[_])
case class HandleAction[A](request: Request[A], response: Response, action: Action[A], app: Application)
-
- val system = {
+
+ // --
+
+ // Call init to register an Actor System properly configured from
+ // this applicationProvider. Otherwise a default ActorSystem will be created.
+ def init(applicationProvider: ApplicationProvider) {
val conf = play.api.Play.maybeApplication.filter(_.mode == Mode.Prod).map(app =>
- ConfigFactory.load()).getOrElse(Configuration.loadDev)
- ActorSystem("play", conf.getConfig("play"))
+ ConfigFactory.load()).getOrElse(Configuration.loadDev(applicationProvider.path))
+ configuredSystem = ActorSystem("play", conf.getConfig("play"))
+ promiseInvoker
+ actionInvoker
+ }
+
+ private var configuredSystem: ActorSystem = _
+
+ // --
+
+ lazy val system = {
+ Option(configuredSystem).getOrElse {
+ Logger.warn("Missing configuration for Play ActorSystem. Starting a default one.")
+ ActorSystem("play")
+ }
}
- val promiseInvoker = {
+ lazy val promiseInvoker = {
system.actorOf(Props[play.api.libs.concurrent.STMPromise.PromiseInvoker].withDispatcher("akka.actor.promises-dispatcher").withRouter(RoundRobinRouter(100)), name = "promises")
}
- val actionInvoker = {
+ lazy val actionInvoker = {
system.actorOf(Props[ActionInvoker].withDispatcher("akka.actor.actions-dispatcher").withRouter(RoundRobinRouter(100)), name = "actions")
}
View
3  framework/src/sbt-plugin/src/main/scala/PlayCommands.scala
@@ -598,7 +598,8 @@ exec java $* -cp "`dirname $0`/lib/*" """ + config.map(_ => "-Dconfig.file=`dirn
val maybeNewState = Project.runTask(dependencyClasspath in Compile, state).get._2.toEither.right.map { dependencies =>
- val classpath = dependencies.map(_.data.toURI.toURL).toArray
+ // All jar dependencies. They will not been reloaded and must be part of this top classloader
+ val classpath = dependencies.map(_.data.toURI.toURL).filter(_.toString.endsWith(".jar")).toArray
/**
* Create a temporary classloader to run the application.
View
14 framework/src/sbt-plugin/src/main/scala/PlayReloader.scala
@@ -181,11 +181,23 @@ trait PlayReloader {
}
}).map(remapProblemForGeneratedSources)
}
+
+ private val classLoaderVersion = new java.util.concurrent.atomic.AtomicInteger(0)
private def newClassLoader = {
val loader = new java.net.URLClassLoader(
Project.evaluateTask(dependencyClasspath in Runtime, state).get.toEither.right.get.map(_.data.toURI.toURL).toArray,
- baseLoader)
+ baseLoader) {
+
+ val version = classLoaderVersion.incrementAndGet
+
+ override def toString = {
+ "ReloadableClassLoader(v" + version + ") {" + {
+ getURLs.map(_.toString).mkString(", ")
+ } + "}"
+ }
+
+ }
currentApplicationClassLoader = Some(loader)
loader
}
View
2  framework/test/integrationtest/app/controllers/Application.scala
@@ -33,7 +33,7 @@ object Application extends Controller {
def conf = Action {
val config = play.api.Play.configuration
- val overrideConfig = play.api.Configuration.load().getInt("playcore.invoker.max.try").get
+ val overrideConfig = play.api.Configuration.load(new java.io.File(".")).getInt("playcore.invoker.max.try").get
val s = config.getString("complex-app.something").getOrElse("boooooo")
val c = config.getString("nokey").getOrElse("None")

No commit comments for this range

Something went wrong with that request. Please try again.