mirror of https://github.com/sbt/sbt.git
* Remove -linksource as a default option for doc
* First attempt at fixing webapp classloading * Additional tests
This commit is contained in:
parent
e02afb36c8
commit
791c7a2a14
|
|
@ -75,7 +75,6 @@ abstract class BasicScalaProject extends ScalaProject with BasicDependencyProjec
|
|||
|
||||
/** The options provided to the 'doc' and 'docTest' actions.*/
|
||||
def documentOptions: Seq[ScaladocOption] =
|
||||
LinkSource ::
|
||||
documentTitle(name + " " + version + " API") ::
|
||||
windowTitle(name + " " + version + " API") ::
|
||||
Nil
|
||||
|
|
@ -404,6 +403,7 @@ abstract class BasicWebScalaProject extends BasicScalaProject with WebScalaProje
|
|||
/** The port that Jetty runs on. */
|
||||
def jettyPort: Int = JettyRunner.DefaultPort
|
||||
|
||||
lazy val jettyReload = task { jettyInstance.reload(); None } describedAs(JettyReloadDescription)
|
||||
lazy val jettyRestart = jettyStop && jettyRun
|
||||
lazy val jettyStop = jettyStopAction
|
||||
protected def jettyStopAction = jettyStopTask(jettyInstance) describedAs(JettyStopDescription)
|
||||
|
|
@ -489,6 +489,8 @@ object BasicWebScalaProject
|
|||
"Starts the Jetty server and serves this project as a web application."
|
||||
val JettyDescription =
|
||||
"Starts the Jetty server and serves this project as a web application. Waits until interrupted, so it is suitable to call this batch-style."
|
||||
val JettyReloadDescription =
|
||||
"Forces a reload of a web application running in a Jetty server started by 'jetty-run'. Does nothing if Jetty is not running."
|
||||
}
|
||||
/** Analyzes the dependencies of a project after compilation. All methods except `snapshot` return a
|
||||
* `PathFinder`. The underlying calculations are repeated for each call to PathFinder.get. */
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ class JettyRunner(configuration: JettyConfiguration) extends ExitHook
|
|||
running.foreach(_.stop())
|
||||
running = None
|
||||
}
|
||||
def reload() = running.foreach(_.reload())
|
||||
def apply(): Option[String] =
|
||||
{
|
||||
import configuration._
|
||||
|
|
@ -32,10 +33,17 @@ class JettyRunner(configuration: JettyConfiguration) extends ExitHook
|
|||
{
|
||||
val baseLoader = this.getClass.getClassLoader
|
||||
val classpathURLs = jettyClasspath.get.map(_.asURL).toSeq
|
||||
val loader: ClassLoader = new java.net.URLClassLoader(classpathURLs.toArray, baseLoader)
|
||||
val lazyLoader = new LazyFrameworkLoader(implClassName, Array(FileUtilities.classLocation[Stoppable].toURI.toURL), loader, baseLoader)
|
||||
val jettyParentLoader = configuration match { case d: DefaultJettyConfiguration => d.parentLoader; case _ => ClassLoader.getSystemClassLoader }
|
||||
val jettyLoader: ClassLoader = new java.net.URLClassLoader(classpathURLs.toArray, jettyParentLoader)
|
||||
|
||||
val jettyFilter = (name: String) => name.startsWith("org.mortbay.")
|
||||
val notJettyFilter = (name: String) => !jettyFilter(name)
|
||||
|
||||
val dual = new xsbt.DualLoader(baseLoader, notJettyFilter, x => true, jettyLoader, jettyFilter, x => false)
|
||||
|
||||
val lazyLoader = new LazyFrameworkLoader(implClassName, Array(FileUtilities.classLocation[Stoppable].toURI.toURL), dual, baseLoader)
|
||||
val runner = ModuleUtilities.getObject(implClassName, lazyLoader).asInstanceOf[JettyRun]
|
||||
runner(configuration)
|
||||
runner(configuration, jettyLoader)
|
||||
}
|
||||
|
||||
if(running.isDefined)
|
||||
|
|
@ -66,10 +74,11 @@ class JettyRunner(configuration: JettyConfiguration) extends ExitHook
|
|||
private trait Stoppable
|
||||
{
|
||||
def stop(): Unit
|
||||
def reload(): Unit
|
||||
}
|
||||
private trait JettyRun
|
||||
{
|
||||
def apply(configuration: JettyConfiguration): Stoppable
|
||||
def apply(configuration: JettyConfiguration, jettyLoader: ClassLoader): Stoppable
|
||||
}
|
||||
sealed trait JettyConfiguration extends NotNull
|
||||
{
|
||||
|
|
@ -105,7 +114,7 @@ private object LazyJettyRun extends JettyRun
|
|||
{
|
||||
import org.mortbay.jetty.{Handler, Server}
|
||||
import org.mortbay.jetty.nio.SelectChannelConnector
|
||||
import org.mortbay.jetty.webapp.WebAppContext
|
||||
import org.mortbay.jetty.webapp.{WebAppClassLoader, WebAppContext}
|
||||
import org.mortbay.log.Log
|
||||
import org.mortbay.util.Scanner
|
||||
import org.mortbay.xml.XmlConfiguration
|
||||
|
|
@ -114,7 +123,7 @@ private object LazyJettyRun extends JettyRun
|
|||
|
||||
val DefaultMaxIdleTime = 30000
|
||||
|
||||
def apply(configuration: JettyConfiguration): Stoppable =
|
||||
def apply(configuration: JettyConfiguration, jettyLoader: ClassLoader): Stoppable =
|
||||
{
|
||||
val oldLog = Log.getLog
|
||||
Log.setLog(new JettyLogger(configuration.log))
|
||||
|
|
@ -127,15 +136,17 @@ private object LazyJettyRun extends JettyRun
|
|||
import c._
|
||||
configureDefaultConnector(server, port)
|
||||
def classpathURLs = classpath.get.map(_.asURL).toSeq
|
||||
def createLoader = new URLClassLoader(classpathURLs.toArray, parentLoader)
|
||||
val webapp = new WebAppContext(war.absolutePath, contextPath)
|
||||
webapp.setClassLoader(createLoader)
|
||||
|
||||
def createLoader = new WebAppClassLoader(jettyLoader, webapp) { override def getURLs = classpathURLs.toArray }
|
||||
def setLoader() = webapp.setClassLoader(createLoader)
|
||||
|
||||
setLoader()
|
||||
server.setHandler(webapp)
|
||||
|
||||
Some(new Scanner.BulkListener {
|
||||
def filesChanged(files: java.util.List[_]) {
|
||||
reload(server, webapp.setClassLoader(createLoader), log)
|
||||
}
|
||||
Some(new Scanner.BulkListener with Reload {
|
||||
def reloadApp() = reload(server, setLoader(), log)
|
||||
def filesChanged(files: java.util.List[_]) { reloadApp() }
|
||||
})
|
||||
case c: CustomJettyConfiguration =>
|
||||
for(x <- c.jettyConfigurationXML)
|
||||
|
|
@ -169,7 +180,7 @@ private object LazyJettyRun extends JettyRun
|
|||
try
|
||||
{
|
||||
server.start()
|
||||
new StopServer(new WeakReference(server), configureScanner(), oldLog)
|
||||
new StopServer(new WeakReference(server), listener.map(new WeakReference(_)), configureScanner(), oldLog)
|
||||
}
|
||||
catch { case e => server.stop(); throw e }
|
||||
}
|
||||
|
|
@ -180,19 +191,16 @@ private object LazyJettyRun extends JettyRun
|
|||
defaultConnector.setMaxIdleTime(DefaultMaxIdleTime)
|
||||
server.addConnector(defaultConnector)
|
||||
}
|
||||
private class StopServer(serverReference: Reference[Server], scannerReferenceOpt: Option[Reference[Scanner]], oldLog: org.mortbay.log.Logger) extends Stoppable
|
||||
trait Reload { def reloadApp(): Unit }
|
||||
private class StopServer(serverReference: Reference[Server], reloadReference: Option[Reference[Reload]], scannerReferenceOpt: Option[Reference[Scanner]], oldLog: org.mortbay.log.Logger) 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()
|
||||
{
|
||||
val server = serverReference.get
|
||||
if(server != null)
|
||||
server.stop()
|
||||
for(scannerReference <- scannerReferenceOpt)
|
||||
{
|
||||
val scanner = scannerReference.get
|
||||
if(scanner != null)
|
||||
scanner.stop()
|
||||
}
|
||||
onReferenced(serverReference.get)(_.stop())
|
||||
on(scannerReferenceOpt)(_.stop())
|
||||
Log.setLog(oldLog)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
project.name=Publish Test
|
||||
project.version=1.0
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
import sbt._
|
||||
|
||||
class Test(info: ProjectInfo) extends DefaultProject(info)
|
||||
{
|
||||
def ivyCacheDirectory = outputPath / "ivy-cache"
|
||||
override def updateOptions = CacheDirectory(ivyCacheDirectory) :: super.updateOptions.toList
|
||||
|
||||
override def managedStyle = ManagedStyle.Maven
|
||||
def testRepoPath = path("test-repo")
|
||||
val publishTo = Resolver.file("test repo", testRepoPath asFile)
|
||||
|
||||
def srcExt = "-sources.jar"
|
||||
def srcFilter = extFilter(srcExt)
|
||||
def docExt = "-javadoc.jar"
|
||||
def docFilter = extFilter(docExt)
|
||||
def extFilter(ext: String) = "*" + ext
|
||||
|
||||
override def packageDocsJar = defaultJarPath(docExt)
|
||||
override def packageSrcJar= defaultJarPath(srcExt)
|
||||
|
||||
val sourceArtifact = Artifact(artifactID, "src", "jar", "sources")
|
||||
val docsArtifact = Artifact(artifactID, "docs", "jar", Some("javadoc"), Nil, None)
|
||||
override def packageToPublishActions = super.packageToPublishActions ++ Seq(packageDocs, packageSrc)
|
||||
|
||||
lazy val check = task { check0 }
|
||||
|
||||
def check0 = checkPom orElse checkBin orElse checkSource orElse checkDoc
|
||||
def checkPom = exists("pom", "*.pom")
|
||||
def checkDoc = exists("javadoc", docFilter)
|
||||
def checkSource = exists("sources", srcFilter)
|
||||
def checkBin = exists("binary", "*.jar" - (srcFilter | docFilter))
|
||||
def exists(label: String, filter: sbt.NameFilter) =
|
||||
if( (testRepoPath ** filter).get.isEmpty) Some("No " + label + " published") else None
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
class TestClass
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
-> check
|
||||
> publish
|
||||
> check
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<html>
|
||||
<body>
|
||||
<% out.println(" Hello World!"); %>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -9,20 +9,12 @@ class JSP(info: ProjectInfo) extends DefaultWebProject(info)
|
|||
def indexFile = new java.io.File("index.html")
|
||||
import Process._
|
||||
lazy val getPage = execTask { indexURL #> indexFile }
|
||||
lazy val checkPage = task { checkHelloWorld() } dependsOn getPage
|
||||
|
||||
private def checkHelloWorld() =
|
||||
lazy val checkPage = task { args => task { checkHelloWorld(args.mkString(" ")) } dependsOn getPage }
|
||||
|
||||
private def checkHelloWorld(checkString: String) =
|
||||
{
|
||||
try
|
||||
{
|
||||
FileUtilities.readString(indexFile, log) match
|
||||
{
|
||||
case Right(value) =>
|
||||
if(value.contains("Hello World!")) None
|
||||
else Some("index.html did not contain 'Hello World!' :\n" +value)
|
||||
case Left(msg) => Some(msg)
|
||||
}
|
||||
}
|
||||
finally { jettyInstance.stop() }
|
||||
val value = xsbt.FileUtilities.read(indexFile)
|
||||
if(value.contains(checkString)) None else Some("index.html did not contain '" + checkString + "' :\n" +value)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,12 @@
|
|||
> update
|
||||
> jetty-run
|
||||
> check-page
|
||||
> jetty-stop
|
||||
> check-page "Hello World!"
|
||||
|
||||
$ copy-file changes/index.jsp src/main/webapp/index.jsp
|
||||
|
||||
> prepare-webapp
|
||||
> jetty-reload
|
||||
> check-page "Hello World 2!"
|
||||
|
||||
> jetty-stop
|
||||
-> check-page "Hello World 2!"
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package test
|
||||
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
class MyServlet extends HttpServlet {
|
||||
|
||||
val html = <HTML>
|
||||
<HEAD><TITLE>Hello, Scala 2!</TITLE></HEAD>
|
||||
<BODY>Hello, Scala 2! This is a servlet.</BODY>
|
||||
</HTML>
|
||||
|
||||
override def doGet(req:HttpServletRequest, resp:HttpServletResponse) {
|
||||
resp.setContentType("text/html")
|
||||
resp.getWriter().print(html.toString)
|
||||
}
|
||||
def check28(f: Int = 3) = f
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
project.organization=tester
|
||||
project.name=sbtweb
|
||||
project.version=1.0
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
import sbt._
|
||||
|
||||
class WebappBuild(info: ProjectInfo) extends DefaultWebProject(info) {
|
||||
|
||||
val servlet_api = "org.mortbay.jetty" % "servlet-api-2.5" % "6.1.14" % "provided->default"
|
||||
val jetty_servlet = "org.mortbay.jetty" % "jetty" % "6.1.14" % "test->default"
|
||||
|
||||
def indexURL = new java.net.URL("http://localhost:8080")
|
||||
def indexFile = new java.io.File("index.html")
|
||||
import Process._
|
||||
lazy val getPage = execTask { indexURL #> indexFile }
|
||||
lazy val checkPage = task { args => task { checkHelloWorld(args.mkString(" ")) } dependsOn getPage }
|
||||
|
||||
private def checkHelloWorld(checkString: String) =
|
||||
{
|
||||
val value = xsbt.FileUtilities.read(indexFile)
|
||||
if(value.contains(checkString)) None else Some("index.html did not contain '" + checkString + "' :\n" +value)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package test
|
||||
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
class MyServlet extends HttpServlet {
|
||||
|
||||
val html = <HTML>
|
||||
<HEAD><TITLE>Hello, Scala!</TITLE></HEAD>
|
||||
<BODY>Hello, Scala! This is a servlet.</BODY>
|
||||
</HTML>
|
||||
|
||||
override def doGet(req:HttpServletRequest, resp:HttpServletResponse) {
|
||||
resp.setContentType("text/html")
|
||||
resp.getWriter().print(html.toString)
|
||||
}
|
||||
def check28(f: Int = 3) = f
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<!DOCTYPE web-app PUBLIC
|
||||
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
|
||||
"http://java.sun.com/dtd/web-app_2_3.dtd" >
|
||||
|
||||
<web-app>
|
||||
<display-name>Archetype Created Web Application</display-name>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>Home</servlet-name>
|
||||
<servlet-class>test.MyServlet</servlet-class>
|
||||
</servlet>
|
||||
<servlet-mapping>
|
||||
<servlet-name>Home</servlet-name>
|
||||
<url-pattern>/</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
</web-app>
|
||||
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
> ++2.8.0.Beta1-RC6
|
||||
> update
|
||||
> jetty-run
|
||||
> check-page "Hello, Scala!"
|
||||
|
||||
$ copy-file changes/MyServlet.scala src/main/scala/test/MyServlet.scala
|
||||
|
||||
> prepare-webapp
|
||||
> jetty-reload
|
||||
> check-page "Hello, Scala 2!"
|
||||
|
||||
> jetty-stop
|
||||
-> check-page "Hello World 2!"
|
||||
Loading…
Reference in New Issue