Skip to content

Commit c324e1d

Browse files
ajithmesrowen
authored andcommitted
[SPARK-27122][CORE] Jetty classes must not be return via getters in org.apache.spark.ui.WebUI
## What changes were proposed in this pull request? When we run YarnSchedulerBackendSuite, the class path seems to be made from the classes folder(resource-managers/yarn/target/scala-2.12/classes) instead of jar (resource-managers/yarn/target/spark-yarn_2.12-3.0.0-SNAPSHOT.jar) . ui.getHandlers is in spark-core and its loaded from spark-core.jar which is shaded and hence refers to org.spark_project.jetty.servlet.ServletContextHandler Here in org.apache.spark.scheduler.cluster.YarnSchedulerBackend, as its not shaded, it expects org.eclipse.jetty.servlet.ServletContextHandler Refer discussion https://issues.apache.org/jira/browse/SPARK-27122?focusedCommentId=16792318&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-16792318 Hence as a fix, org.apache.spark.ui.WebUI must only return a wrapper class instance or references so that Jetty classes can be avoided in getters which are accessed outside spark-core ## How was this patch tested? Existing UT can pass Closes #24088 from ajithme/shadebug. Authored-by: Ajith <ajith2489@gmail.com> Signed-off-by: Sean Owen <sean.owen@databricks.com>
1 parent 4132c98 commit c324e1d

File tree

3 files changed

+51
-22
lines changed

3 files changed

+51
-22
lines changed

core/src/main/scala/org/apache/spark/ui/WebUI.scala

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,15 @@
1717

1818
package org.apache.spark.ui
1919

20-
import javax.servlet.http.HttpServletRequest
20+
import java.util.EnumSet
21+
import javax.servlet.DispatcherType
22+
import javax.servlet.http.{HttpServlet, HttpServletRequest}
2123

2224
import scala.collection.mutable.ArrayBuffer
2325
import scala.collection.mutable.HashMap
2426
import scala.xml.Node
2527

26-
import org.eclipse.jetty.servlet.ServletContextHandler
28+
import org.eclipse.jetty.servlet.{FilterHolder, FilterMapping, ServletContextHandler, ServletHolder}
2729
import org.json4s.JsonAST.{JNothing, JValue}
2830

2931
import org.apache.spark.{SecurityManager, SparkConf, SSLOptions}
@@ -59,6 +61,10 @@ private[spark] abstract class WebUI(
5961
def getTabs: Seq[WebUITab] = tabs
6062
def getHandlers: Seq[ServletContextHandler] = handlers
6163

64+
def getDelegatingHandlers: Seq[DelegatingServletContextHandler] = {
65+
handlers.map(new DelegatingServletContextHandler(_))
66+
}
67+
6268
/** Attaches a tab to this UI, along with all of its attached pages. */
6369
def attachTab(tab: WebUITab): Unit = {
6470
tab.pages.foreach(attachPage)
@@ -95,6 +101,14 @@ private[spark] abstract class WebUI(
95101
serverInfo.foreach(_.addHandler(handler, securityManager))
96102
}
97103

104+
/** Attaches a handler to this UI. */
105+
def attachHandler(contextPath: String, httpServlet: HttpServlet, pathSpec: String): Unit = {
106+
val ctx = new ServletContextHandler()
107+
ctx.setContextPath(contextPath)
108+
ctx.addServlet(new ServletHolder(httpServlet), pathSpec)
109+
attachHandler(ctx)
110+
}
111+
98112
/** Detaches a handler from this UI. */
99113
def detachHandler(handler: ServletContextHandler): Unit = synchronized {
100114
handlers -= handler
@@ -193,3 +207,32 @@ private[spark] abstract class WebUIPage(var prefix: String) {
193207
def render(request: HttpServletRequest): Seq[Node]
194208
def renderJson(request: HttpServletRequest): JValue = JNothing
195209
}
210+
211+
private[spark] class DelegatingServletContextHandler(handler: ServletContextHandler) {
212+
213+
def prependFilterMapping(
214+
filterName: String,
215+
spec: String,
216+
types: EnumSet[DispatcherType]): Unit = {
217+
val mapping = new FilterMapping()
218+
mapping.setFilterName(filterName)
219+
mapping.setPathSpec(spec)
220+
mapping.setDispatcherTypes(types)
221+
handler.getServletHandler.prependFilterMapping(mapping)
222+
}
223+
224+
def addFilter(
225+
filterName: String,
226+
className: String,
227+
filterParams: Map[String, String]): Unit = {
228+
val filterHolder = new FilterHolder()
229+
filterHolder.setName(filterName)
230+
filterHolder.setClassName(className)
231+
filterParams.foreach { case (k, v) => filterHolder.setInitParameter(k, v) }
232+
handler.getServletHandler.addFilter(filterHolder)
233+
}
234+
235+
def filterCount(): Int = {
236+
handler.getServletHandler.getFilters.length
237+
}
238+
}

resource-managers/yarn/src/main/scala/org/apache/spark/scheduler/cluster/YarnSchedulerBackend.scala

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -180,19 +180,9 @@ private[spark] abstract class YarnSchedulerBackend(
180180
}
181181
conf.set(UI_FILTERS, allFilters)
182182

183-
ui.getHandlers.map(_.getServletHandler()).foreach { h =>
184-
val holder = new FilterHolder()
185-
holder.setName(filterName)
186-
holder.setClassName(filterName)
187-
filterParams.foreach { case (k, v) => holder.setInitParameter(k, v) }
188-
h.addFilter(holder)
189-
190-
val mapping = new FilterMapping()
191-
mapping.setFilterName(filterName)
192-
mapping.setPathSpec("/*")
193-
mapping.setDispatcherTypes(EnumSet.allOf(classOf[DispatcherType]))
194-
195-
h.prependFilterMapping(mapping)
183+
ui.getDelegatingHandlers.foreach { h =>
184+
h.addFilter(filterName, filterName, filterParams)
185+
h.prependFilterMapping(filterName, "/*", EnumSet.allOf(classOf[DispatcherType]))
196186
}
197187
}
198188
}

resource-managers/yarn/src/test/scala/org/apache/spark/scheduler/cluster/YarnSchedulerBackendSuite.scala

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,9 @@ class YarnSchedulerBackendSuite extends SparkFunSuite with MockitoSugar with Loc
101101
yarnSchedulerBackend.addWebUIFilter(classOf[TestFilter2].getName(),
102102
Map("responseCode" -> HttpServletResponse.SC_NOT_ACCEPTABLE.toString), "")
103103

104-
sc.ui.get.getHandlers.foreach { h =>
104+
sc.ui.get.getDelegatingHandlers.foreach { h =>
105105
// Two filters above + security filter.
106-
assert(h.getServletHandler().getFilters().length === 3)
106+
assert(h.filterCount() === 3)
107107
}
108108

109109
// The filter should have been added first in the chain, so we should get SC_NOT_ACCEPTABLE
@@ -117,11 +117,7 @@ class YarnSchedulerBackendSuite extends SparkFunSuite with MockitoSugar with Loc
117117
}
118118
}
119119

120-
val ctx = new ServletContextHandler()
121-
ctx.setContextPath("/new-handler")
122-
ctx.addServlet(new ServletHolder(servlet), "/")
123-
124-
sc.ui.get.attachHandler(ctx)
120+
sc.ui.get.attachHandler("/new-handler", servlet, "/")
125121

126122
val newUrl = new URL(sc.uiWebUrl.get + "/new-handler/")
127123
assert(TestUtils.httpResponseCode(newUrl) === HttpServletResponse.SC_NOT_ACCEPTABLE)

0 commit comments

Comments
 (0)