sbt/web/LazyJettyRun.scala.templ

145 lines
5.0 KiB
Plaintext
Raw Normal View History

/* sbt -- Simple Build Tool
* Copyright 2008, 2009, 2010 Mark Harrah
*/
package sbt
package jetty
2010-02-02 00:21:51 +01:00
import java.io.File
import java.net.URL
/* This class starts Jetty.
* NOTE: DO NOT actively use this class. You will see NoClassDefFoundErrors if you fail
2010-02-02 02:48:51 +01:00
* to do so. Only use its name in JettyRun for reflective loading. This allows using
2010-02-02 00:21:51 +01:00
* the Jetty libraries provided on the project classpath instead of requiring them to be
* available on sbt's classpath at startup.
*/
private object LazyJettyRun${jetty.version} extends JettyRun
{
${jetty.imports}
import java.lang.ref.{Reference, WeakReference}
2010-02-16 02:06:17 +01:00
// Jetty classes must be loaded on initialization in order for the version detection code in WebApp to work properly
// this forces them to be loaded- otherwise, it is possible they aren't loaded until 'apply' is called
private[this] val forceJettyLoad = classOf[Server]
2010-02-02 00:21:51 +01:00
val DefaultMaxIdleTime = 30000
def apply(configuration: JettyConfiguration, jettyLoader: ClassLoader): Stoppable =
{
val oldLog = Log.getLog
Log.setLog(new JettyLogger(configuration.log))
val server = new Server
def configureScanner(listener: Scanner.BulkListener, scanDirectories: Seq[File], scanInterval: Int) =
{
if(scanDirectories.isEmpty)
None
else
{
configuration.log.debug("Scanning for changes to: " + scanDirectories.mkString(", "))
val scanner = new Scanner
val list = new java.util.ArrayList[File]
scanDirectories.foreach(x => list.add(x))
scanner.setScanDirs(list)
scanner.setRecursive(true)
scanner.setScanInterval(scanInterval)
scanner.setReportExistingFilesOnStartup(false)
scanner.addListener(listener)
scanner.start()
Some(new WeakReference(scanner))
}
}
val (listener, scanner) =
configuration match
{
case c: DefaultJettyConfiguration =>
import c._
configureDefaultConnector(server, port)
val webapp = new WebAppContext(war.absolutePath, contextPath)
webDefaultXml.foreach{webDefaultXml:File => webapp.setDefaultsDescriptor(webDefaultXml.toString)}
2010-02-02 00:21:51 +01:00
def createLoader =
{
// Jetty treats WebAppClassLoader subclasses specially and we need this special behavior.
// However, Jetty adds extra classpath components directly using 'addURL'.
// We only want the 'urls' we provide in the constructor, so 'addURL' is overridden to do nothing.
class SbtWebAppLoader(urls: Seq[URL]) extends WebAppClassLoader(jettyLoader, webapp)
{
urls.foreach(super.addURL)
override def addURL(u: URL) = {}
}
new SbtWebAppLoader(classpath.getURLs)
2010-02-02 00:21:51 +01:00
}
def setLoader() = webapp.setClassLoader(createLoader)
setLoader()
server.setHandler(webapp)
val listener = new Scanner.BulkListener with Reload {
def reloadApp() = reload(server, setLoader(), log)
def filesChanged(files: java.util.List[_]) { reloadApp() }
}
(Some(listener), configureScanner(listener, c.scanDirectories, c.scanInterval))
case c: CustomJettyConfiguration =>
for(x <- c.jettyConfigurationXML)
(new XmlConfiguration(x.toString)).configure(server)
for(file <- c.jettyConfigurationFiles)
(new XmlConfiguration(file.toURI.toURL)).configure(server)
(None, None)
}
try
{
server.start()
new StopServer(new WeakReference(server), listener.map(new WeakReference(_)), scanner, oldLog)
}
catch { case e => server.stop(); throw e }
}
private def configureDefaultConnector(server: Server, port: Int)
{
val defaultConnector = new SelectChannelConnector
defaultConnector.setPort(port)
defaultConnector.setMaxIdleTime(DefaultMaxIdleTime)
server.addConnector(defaultConnector)
}
trait Reload { def reloadApp(): Unit }
private class StopServer(serverReference: Reference[Server], reloadReference: Option[Reference[Reload]], scannerReferenceOpt: Option[Reference[Scanner]], oldLog: JLogger) extends Stoppable
{
def reload(): Unit = on(reloadReference)(_.reloadApp())
private def on[T](refOpt: Option[Reference[T]])(f: T => Unit): Unit = refOpt.foreach(ref => onReferenced(ref.get)(f))
private def onReferenced[T](t: T)(f: T => Unit): Unit = if(t == null) () else f(t)
def stop()
{
onReferenced(serverReference.get)(_.stop())
on(scannerReferenceOpt)(_.stop())
Log.setLog(oldLog)
}
}
private def reload(server: Server, reconfigure: => Unit, log: Logger)
{
log.info("Reloading web application...")
val handlers = wrapNull(server.getHandlers, server.getHandler)
log.debug("Stopping handlers: " + handlers.mkString(", "))
handlers.foreach(_.stop)
log.debug("Reconfiguring...")
reconfigure
log.debug("Restarting handlers: " + handlers.mkString(", "))
handlers.foreach(_.start)
log.info("Reload complete.")
}
private def wrapNull(a: Array[Handler], b: Handler) =
(a, b) match
{
case (null, null) => Nil
case (null, notB) => notB :: Nil
case (notA, null) => notA.toList
case (notA, notB) => notB :: notA.toList
}
private class JettyLogger(delegate: AbstractLogger) extends JettyLoggerBase(delegate) with JLogger
2010-02-02 00:21:51 +01:00
{
def getLogger(name: String) = this
}
}