-
Notifications
You must be signed in to change notification settings - Fork 28.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[SPARK-18646][REPL] Set parent classloader as null for ExecutorClassLoader #17074
Changes from all commits
e0d9538
0a490bb
13b0167
d66ad0c
92c810f
f9204c2
f9f9770
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,13 +38,15 @@ import org.apache.spark.util.{ParentClassLoader, Utils} | |
* Allows the user to specify if user class path should be first. | ||
* This class loader delegates getting/finding resources to parent loader, | ||
* which makes sense until REPL never provide resource dynamically. | ||
* This class does not set parent classloader since this class loader | ||
* has higher precedence over its parent class loader. | ||
*/ | ||
class ExecutorClassLoader( | ||
conf: SparkConf, | ||
env: SparkEnv, | ||
classUri: String, | ||
parent: ClassLoader, | ||
userClassPathFirst: Boolean) extends ClassLoader with Logging { | ||
userClassPathFirst: Boolean) extends ClassLoader(null) with Logging { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe add comment over this to explain why we are setting a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. added to the description of this class |
||
val uri = new URI(classUri) | ||
val directory = uri.getPath | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,6 +23,8 @@ import java.nio.channels.{FileChannel, ReadableByteChannel} | |
import java.nio.charset.StandardCharsets | ||
import java.nio.file.{Paths, StandardOpenOption} | ||
import java.util | ||
import java.util.Collections | ||
import javax.tools.{JavaFileObject, SimpleJavaFileObject, ToolProvider} | ||
|
||
import scala.io.Source | ||
import scala.language.implicitConversions | ||
|
@@ -77,6 +79,50 @@ class ExecutorClassLoaderSuite | |
} | ||
} | ||
|
||
test("child over system classloader") { | ||
// JavaFileObject for scala.Option class | ||
val scalaOptionFile = new SimpleJavaFileObject( | ||
URI.create(s"string:///scala/Option.java"), | ||
JavaFileObject.Kind.SOURCE) { | ||
|
||
override def getCharContent(ignoreEncodingErrors: Boolean): CharSequence = { | ||
"package scala; class Option {}" | ||
} | ||
} | ||
// compile fake scala.Option class | ||
ToolProvider | ||
.getSystemJavaCompiler | ||
.getTask(null, null, null, null, null, Collections.singletonList(scalaOptionFile)).call() | ||
|
||
// create 'scala' dir in tempDir1 | ||
val scalaDir = new File(tempDir1, "scala") | ||
assert(scalaDir.mkdir(), s"Failed to create 'scala' directory in $tempDir1") | ||
|
||
// move the generated class into scala dir | ||
val filename = "Option.class" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it's because createCompiledClass doesn't handle package name, it needs to put the generated class under 'scala' package and also it needs to move the generated class to 'scala' folder. Since createCompiledClass is widely used, I didn't want to touch it just for this very unique situation. |
||
val result = new File(filename) | ||
assert(result.exists(), "Compiled file not found: " + result.getAbsolutePath) | ||
|
||
val out = new File(scalaDir, filename) | ||
Files.move(result, out) | ||
assert(out.exists(), "Destination file not moved: " + out.getAbsolutePath) | ||
|
||
// construct class loader tree | ||
val parentLoader = new URLClassLoader(urls2, null) | ||
val classLoader = new ExecutorClassLoader( | ||
new SparkConf(), null, url1, parentLoader, true) | ||
|
||
// load 'scala.Option', using ClassforName to do the exact same behavior as | ||
// what JavaDeserializationStream does | ||
|
||
// scalastyle:off classforname | ||
val optionClass = Class.forName("scala.Option", false, classLoader) | ||
// scalastyle:on classforname | ||
|
||
assert(optionClass.getClassLoader == classLoader, | ||
"scala.Option didn't come from ExecutorClassLoader") | ||
} | ||
|
||
test("child first") { | ||
val parentLoader = new URLClassLoader(urls2, null) | ||
val classLoader = new ExecutorClassLoader(new SparkConf(), null, url1, parentLoader, true) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about rephrase to:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also add a empty line before this statement.