Skip to content

Commit 7d57444

Browse files
committed
Refactoring the UI interface to add flexibility
This commit introduces three (abstract) classes: WebUI, UITab, and UIPage. The top of the hierarchy is the WebUI, which contains many tabs and pages. Each tab in turn contains many pages. When a UITab is attached to a WebUI, the WebUI creates a handler for each of the tab's pages. Similarly, when a UIPage is attached to a WebUI, its handler is created. The server in WebUI is then ready to be bound to a host and a port. This commit also breaks down a couple of unnecessarily large files by moving certain classes to their own files.
1 parent ada310a commit 7d57444

26 files changed

+583
-501
lines changed

core/src/main/scala/org/apache/spark/SparkContext.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,8 @@ class SparkContext(
158158

159159
// Initialize the Spark UI, registering all associated listeners
160160
private[spark] val ui = new SparkUI(this)
161-
ui.bind()
162161
ui.start()
162+
ui.bind()
163163

164164
// Optionally log Spark events
165165
private[spark] val eventLogger: Option[EventLoggingListener] = {

core/src/main/scala/org/apache/spark/deploy/master/Master.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ private[spark] class Master(
8585
val masterSource = new MasterSource(this)
8686

8787
val webUi = new MasterWebUI(this, webUiPort)
88+
webUi.start()
8889

8990
val masterPublicAddress = {
9091
val envVar = System.getenv("SPARK_PUBLIC_DNS")

core/src/main/scala/org/apache/spark/deploy/master/ui/ApplicationPage.scala

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,17 @@ import org.json4s.JValue
2828
import org.apache.spark.deploy.JsonProtocol
2929
import org.apache.spark.deploy.DeployMessages.{MasterStateResponse, RequestMasterState}
3030
import org.apache.spark.deploy.master.ExecutorInfo
31-
import org.apache.spark.ui.UIUtils
31+
import org.apache.spark.ui.{UIPage, UIUtils}
3232
import org.apache.spark.util.Utils
3333

34-
private[spark] class ApplicationPage(parent: MasterWebUI) {
35-
val master = parent.masterActorRef
36-
val timeout = parent.timeout
34+
private[spark] class ApplicationPage(parent: MasterWebUI)
35+
extends UIPage("app", includeJson = true) {
36+
37+
private val master = parent.masterActorRef
38+
private val timeout = parent.timeout
3739

3840
/** Executor details for a particular application */
39-
def renderJson(request: HttpServletRequest): JValue = {
41+
override def renderJson(request: HttpServletRequest): JValue = {
4042
val appId = request.getParameter("appId")
4143
val stateFuture = (master ? RequestMasterState)(timeout).mapTo[MasterStateResponse]
4244
val state = Await.result(stateFuture, timeout)
@@ -47,7 +49,7 @@ private[spark] class ApplicationPage(parent: MasterWebUI) {
4749
}
4850

4951
/** Executor details for a particular application */
50-
def render(request: HttpServletRequest): Seq[Node] = {
52+
override def render(request: HttpServletRequest): Seq[Node] = {
5153
val appId = request.getParameter("appId")
5254
val stateFuture = (master ? RequestMasterState)(timeout).mapTo[MasterStateResponse]
5355
val state = Await.result(stateFuture, timeout)
@@ -96,7 +98,7 @@ private[spark] class ApplicationPage(parent: MasterWebUI) {
9698
UIUtils.basicSparkPage(content, "Application: " + app.desc.name)
9799
}
98100

99-
def executorRow(executor: ExecutorInfo): Seq[Node] = {
101+
private def executorRow(executor: ExecutorInfo): Seq[Node] = {
100102
<tr>
101103
<td>{executor.id}</td>
102104
<td>

core/src/main/scala/org/apache/spark/deploy/master/ui/IndexPage.scala

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,24 +25,24 @@ import scala.xml.Node
2525
import akka.pattern.ask
2626
import org.json4s.JValue
2727

28-
import org.apache.spark.deploy.{JsonProtocol}
28+
import org.apache.spark.deploy.JsonProtocol
2929
import org.apache.spark.deploy.DeployMessages.{MasterStateResponse, RequestMasterState}
3030
import org.apache.spark.deploy.master.{ApplicationInfo, DriverInfo, WorkerInfo}
31-
import org.apache.spark.ui.{WebUI, UIUtils}
31+
import org.apache.spark.ui.{UIPage, UIUtils}
3232
import org.apache.spark.util.Utils
3333

34-
private[spark] class IndexPage(parent: MasterWebUI) {
35-
val master = parent.masterActorRef
36-
val timeout = parent.timeout
34+
private[spark] class IndexPage(parent: MasterWebUI) extends UIPage("", includeJson = true) {
35+
private val master = parent.masterActorRef
36+
private val timeout = parent.timeout
3737

38-
def renderJson(request: HttpServletRequest): JValue = {
38+
override def renderJson(request: HttpServletRequest): JValue = {
3939
val stateFuture = (master ? RequestMasterState)(timeout).mapTo[MasterStateResponse]
4040
val state = Await.result(stateFuture, timeout)
4141
JsonProtocol.writeMasterState(state)
4242
}
4343

4444
/** Index view listing applications and executors */
45-
def render(request: HttpServletRequest): Seq[Node] = {
45+
override def render(request: HttpServletRequest): Seq[Node] = {
4646
val stateFuture = (master ? RequestMasterState)(timeout).mapTo[MasterStateResponse]
4747
val state = Await.result(stateFuture, timeout)
4848

@@ -139,7 +139,7 @@ private[spark] class IndexPage(parent: MasterWebUI) {
139139
UIUtils.basicSparkPage(content, "Spark Master at " + state.uri)
140140
}
141141

142-
def workerRow(worker: WorkerInfo): Seq[Node] = {
142+
private def workerRow(worker: WorkerInfo): Seq[Node] = {
143143
<tr>
144144
<td>
145145
<a href={worker.webUiAddress}>{worker.id}</a>
@@ -154,8 +154,7 @@ private[spark] class IndexPage(parent: MasterWebUI) {
154154
</tr>
155155
}
156156

157-
158-
def appRow(app: ApplicationInfo): Seq[Node] = {
157+
private def appRow(app: ApplicationInfo): Seq[Node] = {
159158
<tr>
160159
<td>
161160
<a href={"app?appId=" + app.id}>{app.id}</a>
@@ -169,14 +168,14 @@ private[spark] class IndexPage(parent: MasterWebUI) {
169168
<td sorttable_customkey={app.desc.memoryPerSlave.toString}>
170169
{Utils.megabytesToString(app.desc.memoryPerSlave)}
171170
</td>
172-
<td>{WebUI.formatDate(app.submitDate)}</td>
171+
<td>{UIUtils.formatDate(app.submitDate)}</td>
173172
<td>{app.desc.user}</td>
174173
<td>{app.state.toString}</td>
175-
<td>{WebUI.formatDuration(app.duration)}</td>
174+
<td>{UIUtils.formatDuration(app.duration)}</td>
176175
</tr>
177176
}
178177

179-
def driverRow(driver: DriverInfo): Seq[Node] = {
178+
private def driverRow(driver: DriverInfo): Seq[Node] = {
180179
<tr>
181180
<td>{driver.id} </td>
182181
<td>{driver.submitDate}</td>

core/src/main/scala/org/apache/spark/deploy/master/ui/MasterWebUI.scala

Lines changed: 15 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -17,64 +17,49 @@
1717

1818
package org.apache.spark.deploy.master.ui
1919

20-
import javax.servlet.http.HttpServletRequest
21-
22-
import org.eclipse.jetty.servlet.ServletContextHandler
23-
2420
import org.apache.spark.Logging
2521
import org.apache.spark.deploy.master.Master
26-
import org.apache.spark.ui.{ServerInfo, SparkUI}
22+
import org.apache.spark.ui.{SparkUI, WebUI}
2723
import org.apache.spark.ui.JettyUtils._
2824
import org.apache.spark.util.{AkkaUtils, Utils}
2925

3026
/**
3127
* Web UI server for the standalone master.
3228
*/
3329
private[spark]
34-
class MasterWebUI(val master: Master, requestedPort: Int) extends Logging {
35-
val masterActorRef = master.self
36-
val timeout = AkkaUtils.askTimeout(master.conf)
30+
class MasterWebUI(val master: Master, requestedPort: Int)
31+
extends WebUI(master.securityMgr) with Logging {
3732

3833
private val host = Utils.localHostName()
3934
private val port = requestedPort
40-
private val applicationPage = new ApplicationPage(this)
41-
private val indexPage = new IndexPage(this)
42-
private var serverInfo: Option[ServerInfo] = None
35+
val masterActorRef = master.self
36+
val timeout = AkkaUtils.askTimeout(master.conf)
4337

44-
private val handlers: Seq[ServletContextHandler] = {
45-
master.masterMetricsSystem.getServletHandlers ++
46-
master.applicationMetricsSystem.getServletHandlers ++
47-
Seq[ServletContextHandler](
48-
createStaticHandler(MasterWebUI.STATIC_RESOURCE_DIR, "/static"),
49-
createServletHandler("/app/json",
50-
(request: HttpServletRequest) => applicationPage.renderJson(request), master.securityMgr),
51-
createServletHandler("/app",
52-
(request: HttpServletRequest) => applicationPage.render(request), master.securityMgr),
53-
createServletHandler("/json",
54-
(request: HttpServletRequest) => indexPage.renderJson(request), master.securityMgr),
55-
createServletHandler("/",
56-
(request: HttpServletRequest) => indexPage.render(request), master.securityMgr)
57-
)
38+
def start() {
39+
attachPage(new ApplicationPage(this))
40+
attachPage(new IndexPage(this))
41+
attachHandler(createStaticHandler(MasterWebUI.STATIC_RESOURCE_DIR, "/static"))
42+
master.masterMetricsSystem.getServletHandlers.foreach(attachHandler)
43+
master.applicationMetricsSystem.getServletHandlers.foreach(attachHandler)
5844
}
5945

46+
/** Bind to the HTTP server behind this web interface. */
6047
def bind() {
6148
try {
6249
serverInfo = Some(startJettyServer(host, port, handlers, master.conf))
6350
logInfo("Started Master web UI at http://%s:%d".format(host, boundPort))
6451
} catch {
6552
case e: Exception =>
66-
logError("Failed to create Master JettyUtils", e)
53+
logError("Failed to create Master web UI", e)
6754
System.exit(1)
6855
}
6956
}
7057

71-
def boundPort: Int = serverInfo.map(_.boundPort).getOrElse(-1)
72-
7358
/** Attach a reconstructed UI to this Master UI. Only valid after bind(). */
7459
def attachUI(ui: SparkUI) {
7560
assert(serverInfo.isDefined, "Master UI must be bound to a server before attaching SparkUIs")
7661
val rootHandler = serverInfo.get.rootHandler
77-
for (handler <- ui.handlers) {
62+
for (handler <- ui.getHandlers) {
7863
rootHandler.addHandler(handler)
7964
if (!handler.isStarted) {
8065
handler.start()
@@ -86,18 +71,13 @@ class MasterWebUI(val master: Master, requestedPort: Int) extends Logging {
8671
def detachUI(ui: SparkUI) {
8772
assert(serverInfo.isDefined, "Master UI must be bound to a server before detaching SparkUIs")
8873
val rootHandler = serverInfo.get.rootHandler
89-
for (handler <- ui.handlers) {
74+
for (handler <- ui.getHandlers) {
9075
if (handler.isStarted) {
9176
handler.stop()
9277
}
9378
rootHandler.removeHandler(handler)
9479
}
9580
}
96-
97-
def stop() {
98-
assert(serverInfo.isDefined, "Attempted to stop a Master UI that was not bound to a server!")
99-
serverInfo.get.server.stop()
100-
}
10181
}
10282

10383
private[spark] object MasterWebUI {

core/src/main/scala/org/apache/spark/deploy/worker/Worker.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,9 @@ private[spark] class Worker(
123123
logInfo("Spark home: " + sparkHome)
124124
createWorkDir()
125125
webUi = new WorkerWebUI(this, workDir, Some(webUiPort))
126-
context.system.eventStream.subscribe(self, classOf[RemotingLifecycleEvent])
126+
webUi.start()
127127
webUi.bind()
128+
context.system.eventStream.subscribe(self, classOf[RemotingLifecycleEvent])
128129
registerWithMaster()
129130

130131
metricsSystem.registerSource(workerSource)

core/src/main/scala/org/apache/spark/deploy/worker/ui/IndexPage.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,21 @@ import org.apache.spark.deploy.JsonProtocol
2828
import org.apache.spark.deploy.DeployMessages.{RequestWorkerState, WorkerStateResponse}
2929
import org.apache.spark.deploy.master.DriverState
3030
import org.apache.spark.deploy.worker.{DriverRunner, ExecutorRunner}
31-
import org.apache.spark.ui.UIUtils
31+
import org.apache.spark.ui.{UIPage, UIUtils}
3232
import org.apache.spark.util.Utils
3333

34-
private[spark] class IndexPage(parent: WorkerWebUI) {
34+
private[spark] class IndexPage(parent: WorkerWebUI) extends UIPage("", includeJson = true) {
3535
val workerActor = parent.worker.self
3636
val worker = parent.worker
3737
val timeout = parent.timeout
3838

39-
def renderJson(request: HttpServletRequest): JValue = {
39+
override def renderJson(request: HttpServletRequest): JValue = {
4040
val stateFuture = (workerActor ? RequestWorkerState)(timeout).mapTo[WorkerStateResponse]
4141
val workerState = Await.result(stateFuture, timeout)
4242
JsonProtocol.writeWorkerState(workerState)
4343
}
4444

45-
def render(request: HttpServletRequest): Seq[Node] = {
45+
override def render(request: HttpServletRequest): Seq[Node] = {
4646
val stateFuture = (workerActor ? RequestWorkerState)(timeout).mapTo[WorkerStateResponse]
4747
val workerState = Await.result(stateFuture, timeout)
4848

0 commit comments

Comments
 (0)