Skip to content

Commit be9dd15

Browse files
HeartSaVioRMarcelo Vanzin
authored andcommitted
[SPARK-11818][REPL] Fix ExecutorClassLoader to lookup resources from …
…parent class loader Without patch, two additional tests of ExecutorClassLoaderSuite fails. - "resource from parent" - "resources from parent" Detailed explanation is here, https://issues.apache.org/jira/browse/SPARK-11818?focusedCommentId=15011202&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-15011202 Author: Jungtaek Lim <kabhwan@gmail.com> Closes #9812 from HeartSaVioR/SPARK-11818.
1 parent 5889880 commit be9dd15

File tree

2 files changed

+40
-1
lines changed

2 files changed

+40
-1
lines changed

repl/src/main/scala/org/apache/spark/repl/ExecutorClassLoader.scala

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ import org.apache.spark.util.ParentClassLoader
3434
/**
3535
* A ClassLoader that reads classes from a Hadoop FileSystem or HTTP URI,
3636
* used to load classes defined by the interpreter when the REPL is used.
37-
* Allows the user to specify if user class path should be first
37+
* Allows the user to specify if user class path should be first.
38+
* This class loader delegates getting/finding resources to parent loader,
39+
* which makes sense until REPL never provide resource dynamically.
3840
*/
3941
class ExecutorClassLoader(conf: SparkConf, classUri: String, parent: ClassLoader,
4042
userClassPathFirst: Boolean) extends ClassLoader with Logging {
@@ -55,6 +57,14 @@ class ExecutorClassLoader(conf: SparkConf, classUri: String, parent: ClassLoader
5557
}
5658
}
5759

60+
override def getResource(name: String): URL = {
61+
parentLoader.getResource(name)
62+
}
63+
64+
override def getResources(name: String): java.util.Enumeration[URL] = {
65+
parentLoader.getResources(name)
66+
}
67+
5868
override def findClass(name: String): Class[_] = {
5969
userClassPathFirst match {
6070
case true => findClassLocally(name).getOrElse(parentLoader.loadClass(name))

repl/src/test/scala/org/apache/spark/repl/ExecutorClassLoaderSuite.scala

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,13 @@ package org.apache.spark.repl
1919

2020
import java.io.File
2121
import java.net.{URL, URLClassLoader}
22+
import java.nio.charset.StandardCharsets
23+
import java.util
24+
25+
import com.google.common.io.Files
2226

2327
import scala.concurrent.duration._
28+
import scala.io.Source
2429
import scala.language.implicitConversions
2530
import scala.language.postfixOps
2631

@@ -41,6 +46,7 @@ class ExecutorClassLoaderSuite
4146

4247
val childClassNames = List("ReplFakeClass1", "ReplFakeClass2")
4348
val parentClassNames = List("ReplFakeClass1", "ReplFakeClass2", "ReplFakeClass3")
49+
val parentResourceNames = List("fake-resource.txt")
4450
var tempDir1: File = _
4551
var tempDir2: File = _
4652
var url1: String = _
@@ -54,6 +60,9 @@ class ExecutorClassLoaderSuite
5460
url1 = "file://" + tempDir1
5561
urls2 = List(tempDir2.toURI.toURL).toArray
5662
childClassNames.foreach(TestUtils.createCompiledClass(_, tempDir1, "1"))
63+
parentResourceNames.foreach { x =>
64+
Files.write("resource".getBytes(StandardCharsets.UTF_8), new File(tempDir2, x))
65+
}
5766
parentClassNames.foreach(TestUtils.createCompiledClass(_, tempDir2, "2"))
5867
}
5968

@@ -99,6 +108,26 @@ class ExecutorClassLoaderSuite
99108
}
100109
}
101110

111+
test("resource from parent") {
112+
val parentLoader = new URLClassLoader(urls2, null)
113+
val classLoader = new ExecutorClassLoader(new SparkConf(), url1, parentLoader, true)
114+
val resourceName: String = parentResourceNames.head
115+
val is = classLoader.getResourceAsStream(resourceName)
116+
assert(is != null, s"Resource $resourceName not found")
117+
val content = Source.fromInputStream(is, "UTF-8").getLines().next()
118+
assert(content.contains("resource"), "File doesn't contain 'resource'")
119+
}
120+
121+
test("resources from parent") {
122+
val parentLoader = new URLClassLoader(urls2, null)
123+
val classLoader = new ExecutorClassLoader(new SparkConf(), url1, parentLoader, true)
124+
val resourceName: String = parentResourceNames.head
125+
val resources: util.Enumeration[URL] = classLoader.getResources(resourceName)
126+
assert(resources.hasMoreElements, s"Resource $resourceName not found")
127+
val fileReader = Source.fromInputStream(resources.nextElement().openStream()).bufferedReader()
128+
assert(fileReader.readLine().contains("resource"), "File doesn't contain 'resource'")
129+
}
130+
102131
test("failing to fetch classes from HTTP server should not leak resources (SPARK-6209)") {
103132
// This is a regression test for SPARK-6209, a bug where each failed attempt to load a class
104133
// from the driver's class server would leak a HTTP connection, causing the class server's

0 commit comments

Comments
 (0)