Github user andrewor14 commented on a diff in the pull request:
https://github.com/apache/spark/pull/290#discussion_r11499492
--- Diff: core/src/main/scala/org/apache/spark/ui/WebUI.scala ---
@@ -17,34 +17,122 @@
package org.apache.spark.ui
-import java.text.SimpleDateFormat
-import java.util.Date
+import javax.servlet.http.HttpServletRequest
+
+import scala.collection.mutable.ArrayBuffer
+import scala.xml.Node
+
+import org.eclipse.jetty.servlet.ServletContextHandler
+import org.json4s.JsonAST.{JNothing, JValue}
+
+import org.apache.spark.SecurityManager
+import org.apache.spark.scheduler.SparkListener
+import org.apache.spark.ui.JettyUtils._
+import org.apache.spark.util.Utils
/**
- * Utilities used throughout the web UI.
+ * The top level component of the UI hierarchy that contains the server.
+ *
+ * Each WebUI represents a collection of tabs, each of which in turn
represents a collection of
+ * pages. The use of tabs is optional, however; a WebUI may choose to
include pages directly.
*/
-private[spark] object WebUI {
- // SimpleDateFormat is not thread-safe. Don't expose it to avoid
improper use.
- private val dateFormat = new ThreadLocal[SimpleDateFormat]() {
- override def initialValue(): SimpleDateFormat = new
SimpleDateFormat("yyyy/MM/dd HH:mm:ss")
- }
+private[spark] abstract class WebUI(securityManager: SecurityManager,
basePath: String = "") {
+ protected val tabs = ArrayBuffer[UITab]()
+ protected val handlers = ArrayBuffer[ServletContextHandler]()
+ protected var serverInfo: Option[ServerInfo] = None
- def formatDate(date: Date): String = dateFormat.get.format(date)
+ def getTabs: Seq[UITab] = tabs.toSeq
+ def getHandlers: Seq[ServletContextHandler] = handlers.toSeq
+ def getListeners: Seq[SparkListener] = tabs.flatMap(_.listener)
- def formatDate(timestamp: Long): String = dateFormat.get.format(new
Date(timestamp))
+ /** Attach a tab to this UI, along with all of its attached pages. */
+ def attachTab(tab: UITab) {
+ tab.start()
+ tab.pages.foreach(attachPage)
+ tabs += tab
+ }
- def formatDuration(milliseconds: Long): String = {
- val seconds = milliseconds.toDouble / 1000
- if (seconds < 60) {
- return "%.0f s".format(seconds)
+ /** Attach a page to this UI. */
+ def attachPage(page: UIPage) {
+ val pagePath = "/" + page.prefix
+ attachHandler(createServletHandler(pagePath,
+ (request: HttpServletRequest) => page.render(request),
securityManager, basePath))
+ if (page.includeJson) {
+ attachHandler(createServletHandler(pagePath.stripSuffix("/") +
"/json",
+ (request: HttpServletRequest) => page.renderJson(request),
securityManager, basePath))
}
- val minutes = seconds / 60
- if (minutes < 10) {
- return "%.1f min".format(minutes)
- } else if (minutes < 60) {
- return "%.0f min".format(minutes)
+ }
+
+ /** Attach a handler to this UI. */
+ def attachHandler(handler: ServletContextHandler) {
+ handlers += handler
+ serverInfo.foreach { info =>
+ info.rootHandler.addHandler(handler)
+ if (!handler.isStarted) {
+ handler.start()
+ }
}
- val hours = minutes / 60
- return "%.1f h".format(hours)
}
+
+ /** Detach a handler from this UI. */
+ def detachHandler(handler: ServletContextHandler) {
+ handlers -= handler
+ serverInfo.foreach { info =>
+ info.rootHandler.removeHandler(handler)
+ if (handler.isStarted) {
+ handler.stop()
+ }
+ }
+ }
+
+ /** Initialize all components of the server. */
+ def start()
+
+ /**
+ * Bind to the HTTP server behind this web interface.
+ * Overridden implementation should set serverInfo.
+ */
+ def bind()
+
+ /** Return the actual port to which this server is bound. Only valid
after bind(). */
+ def boundPort: Int = serverInfo.map(_.boundPort).getOrElse(-1)
+
+ /** Stop the server behind this web interface. Only valid after bind().
*/
+ def stop() {
+ assert(serverInfo.isDefined,
+ "Attempted to stop %s before binding to a
server!".format(Utils.getFormattedClassName(this)))
+ serverInfo.get.server.stop()
+ }
+}
+
+
+/**
+ * A tab that represents a collection of pages and a unit of listening for
Spark events.
+ * Associating each tab with a listener is arbitrary and need not be the
case.
+ */
+private[spark] abstract class UITab(val prefix: String) {
+ val pages = ArrayBuffer[UIPage]()
+ var listener: Option[SparkListener] = None
--- End diff --
I added this, but in retrospect SparkListener should really not be
associated with the UITab at all. I will put in a PR with this change.
---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at [email protected] or file a JIRA ticket
with INFRA.
---