|
Play Framework/Scala example source code file (PlayReloader.scala)
The PlayReloader.scala Play Framework example source code/* * Copyright (C) 2009-2013 Typesafe Inc. <http://www.typesafe.com> */ package play import play.sbtplugin.run.PlayWatchService import play.api._ import play.core._ import sbt._ import sbt.Keys._ import play.PlayImport._ import PlayKeys._ import PlayExceptions._ import play.sbtplugin.PlayPositionMapper trait PlayReloader { this: PlayCommands with PlayPositionMapper => /** * An extension of BuildLink to provide internal methods to PlayRun, such as the ability to get the classloader, * and the ability to close it (stop watching files). */ trait PlayBuildLink extends BuildLink { def close() def getClassLoader: Option[ClassLoader] } /** * Create a new reloader */ def newReloader(state: State, playReload: TaskKey[sbt.inc.Analysis], createClassLoader: ClassLoaderCreator, classpathTask: TaskKey[Classpath], baseLoader: ClassLoader, monitoredFiles: Seq[String], targetDirectory: File): PlayBuildLink = { val extracted = Project.extract(state) new PlayBuildLink { lazy val projectPath = extracted.currentProject.base // The current classloader for the application @volatile private var currentApplicationClassLoader: Option[ClassLoader] = None // Flag to force a reload on the next request. // This is set if a compile error occurs, and also by the forceReload method on BuildLink, which is called for // example when evolutions have been applied. @volatile private var forceReloadNextTime = false // Whether any source files have changed since the last request. @volatile private var changed = false // The last successful compile results. Used for rendering nice errors. @volatile private var currentAnalysis = Option.empty[sbt.inc.Analysis] // A watch state for the classpath. Used to determine whether anything on the classpath has changed as a result // of compilation, and therefore a new classloader is needed and the app needs to be reloaded. @volatile private var watchState: WatchState = WatchState.empty // Create the watcher, updates the changed boolean when a file has changed. private val watcher = PlayWatchService(targetDirectory).watch(monitoredFiles.map(new File(_)), () => changed = true) private val classLoaderVersion = new java.util.concurrent.atomic.AtomicInteger(0) /** * Contrary to its name, this doesn't necessarily reload the app. It is invoked on every request, and will only * trigger a reload of the app if something has changed. * * Since this communicates across classloaders, it must return only simple objects. * * * @return Either * - Throwable - If something went wrong (eg, a compile error). * - ClassLoader - If the classloader has changed, and the application should be reloaded. * - null - If nothing changed. */ def reload: AnyRef = { play.Play.synchronized { if (changed || forceReloadNextTime || currentAnalysis.isEmpty || currentApplicationClassLoader.isEmpty) { val shouldReload = forceReloadNextTime changed = false forceReloadNextTime = false // Run the reload task, which will trigger everything to compile Project.runTask(playReload, state).map(_._2).get.toEither .left.map(taskFailureHandler) .right.map { compilationResult => currentAnalysis = Some(compilationResult) // Calculate the classpath Project.runTask(classpathTask, state).map(_._2).get.toEither .left.map(taskFailureHandler) .right.map { classpath => // We only want to reload if the classpath has changed. Assets don't live on the classpath, so // they won't trigger a reload. // Use the SBT watch service, passing true as the termination to force it to break after one check val (_, newState) = SourceModificationWatch.watch(classpath.files.***, 0, watchState)(true) // SBT has a quiet wait period, if that's set to true, sources were modified val triggered = newState.awaitingQuietPeriod watchState = newState if (triggered || shouldReload || currentApplicationClassLoader.isEmpty) { // Create a new classloader val version = classLoaderVersion.incrementAndGet val name = "ReloadableClassLoader(v" + version + ")" val urls = Path.toURLs(classpath.files) val loader = createClassLoader(name, urls, baseLoader) currentApplicationClassLoader = Some(loader) loader } else { null // null means nothing changed } }.fold(identity, identity) }.fold(identity, identity) } else { null // null means nothing changed } } } lazy val settings = { import scala.collection.JavaConverters._ extracted.get(devSettings).toMap.asJava } def forceReload() { forceReloadNextTime = true } def findSource(className: String, line: java.lang.Integer): Array[java.lang.Object] = { val topType = className.split('$').head currentAnalysis.flatMap { analysis => analysis.apis.internal.flatMap { case (sourceFile, source) => source.api.definitions.find(defined => defined.name == topType).map(_ => { sourceFile: java.io.File } -> line) }.headOption.map { case (source, maybeLine) => play.twirl.compiler.MaybeGeneratedSource.unapply(source).map { generatedSource => generatedSource.source.get -> Option(maybeLine).map(l => generatedSource.mapLine(l): java.lang.Integer).orNull }.getOrElse(source -> maybeLine) } }.map { case (file, l) => Array[java.lang.Object](file, l) }.orNull } def remapProblemForGeneratedSources(problem: xsbti.Problem) = { val mappedPosition = playPositionMapper(problem.position) mappedPosition.map { pos => new xsbti.Problem { def message = problem.message def category = "" def position = pos def severity = problem.severity } } getOrElse problem } private def allProblems(inc: Incomplete): Seq[xsbti.Problem] = { allProblems(inc :: Nil) } private def allProblems(incs: Seq[Incomplete]): Seq[xsbti.Problem] = { problems(Incomplete.allExceptions(incs).toSeq) } private def problems(es: Seq[Throwable]): Seq[xsbti.Problem] = { es flatMap { case cf: xsbti.CompileFailed => cf.problems case _ => Nil } } def getProblems(incomplete: Incomplete): Seq[xsbti.Problem] = { (allProblems(incomplete) ++ { Incomplete.linearize(incomplete).filter(i => i.node.isDefined && i.node.get.isInstanceOf[ScopedKey[_]]).flatMap { i => val JavacError = """\[error\]\s*(.*[.]java):(\d+):\s*(.*)""".r val JavacErrorInfo = """\[error\]\s*([a-z ]+):(.*)""".r val JavacErrorPosition = """\[error\](\s*)\^\s*""".r Project.runTask(streamsManager, state).map(_._2).get.toEither.right.toOption.map { streamsManager => var first: (Option[(String, String, String)], Option[Int]) = (None, None) var parsed: (Option[(String, String, String)], Option[Int]) = (None, None) Output.lastLines(i.node.get.asInstanceOf[ScopedKey[_]], streamsManager, None).map(_.replace(scala.Console.RESET, "")).map(_.replace(scala.Console.RED, "")).collect { case JavacError(file, line, message) => parsed = Some((file, line, message)) -> None case JavacErrorInfo(key, message) => parsed._1.foreach { o => parsed = Some((parsed._1.get._1, parsed._1.get._2, parsed._1.get._3 + " [" + key.trim + ": " + message.trim + "]")) -> None } case JavacErrorPosition(pos) => parsed = parsed._1 -> Some(pos.size) if (first == ((None, None))) { first = parsed } } first }.collect { case (Some(error), maybePosition) => new xsbti.Problem { def message = error._3 def category = "" def position = new xsbti.Position { def line = xsbti.Maybe.just(error._2.toInt) def lineContent = "" def offset = xsbti.Maybe.nothing[java.lang.Integer] def pointer = maybePosition.map(pos => xsbti.Maybe.just((pos - 1).asInstanceOf[java.lang.Integer])).getOrElse(xsbti.Maybe.nothing[java.lang.Integer]) def pointerSpace = xsbti.Maybe.nothing[String] def sourceFile = xsbti.Maybe.just(file(error._1)) def sourcePath = xsbti.Maybe.just(error._1) } def severity = xsbti.Severity.Error } } } }).map(remapProblemForGeneratedSources) } private def taskFailureHandler(incomplete: Incomplete): Exception = { // We force reload next time because compilation failed this time forceReloadNextTime = true Incomplete.allExceptions(incomplete).headOption.map { case e: PlayException => e case e: xsbti.CompileFailed => getProblems(incomplete) .find(_.severity == xsbti.Severity.Error) .map(CompilationException) .getOrElse(UnexpectedException(Some("The compilation failed without reporting any problem!"), Some(e))) case e: Exception => UnexpectedException(unexpected = Some(e)) }.getOrElse { UnexpectedException(Some("The compilation task failed without any exception!")) } } def runTask(task: String): AnyRef = { val parser = Act.scopedKeyParser(state) val Right(sk) = complete.DefaultParsers.result(parser, task) val result = Project.runTask(sk.asInstanceOf[Def.ScopedKey[Task[AnyRef]]], state).map(_._2) result.flatMap(_.toEither.right.toOption).orNull } def close() = { currentApplicationClassLoader = None currentAnalysis = None watcher.stop() } def getClassLoader = currentApplicationClassLoader } } } Other Play Framework source code examplesHere is a short list of links related to this Play Framework PlayReloader.scala source code file: |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.