spark에서 UDF (UserDefinedFunction)을 사용하다 아래와 같은 에러가 발생했다.
Exception in thread "main" java.lang.InternalError: Malformed class name
at java.lang.Class.getSimpleName(Class.java:1330)
at org.apache.spark.sql.execution.aggregate.ScalaUDAF.nodeName(udaf.scala:451)
아래와 같은 UDF 코드인데.
object UDFExampleDemo {
def main(args: Array[String]) {
class BoolAnd extends UserDefinedAggregateFunction {
...
}
...
}
}
아래와 같이 변경해야 에러가 발생하지 않는다.
class BoolAnd extends UserDefinedAggregateFunction {
...
}
object UDFExampleDemo {
def main(args: Array[String]) {
...
}
}
node name을 얻을 려면 udaf의 class의 getSimpleName을 호출한다.
override def nodeName: String = udaf.getClass.getSimpleName
java의 Class코드를 보면,
Malformed class name을 발생하는 코드 앞에 주석 설명이 잘 되어 있다. getSimpleName은 적당한 깊이의 클래스의 simple name을 읽어 오는 것만 허락한다. 따라서 udaf를 상속한 클래스의 위치가 main 안에 두면 안된다.
java.lang.Class의 getSimpleName 코드
public String getSimpleName() {
if (isArray())
return getComponentType().getSimpleName()+"[]";
String simpleName = getSimpleBinaryName();
if (simpleName == null) { // top level class
simpleName = getName();
return simpleName.substring(simpleName.lastIndexOf(".")+1); // strip the package name
}
// According to JLS3 "Binary Compatibility" (13.1) the binary
// name of non-package classes (not top level) is the binary
// name of the immediately enclosing class followed by a '$' followed by:
// (for nested and inner classes): the simple name.
// (for local classes): 1 or more digits followed by the simple name.
// (for anonymous classes): 1 or more digits.
// Since getSimpleBinaryName() will strip the binary name of
// the immediatly enclosing class, we are now looking at a
// string that matches the regular expression "\$[0-9]*"
// followed by a simple name (considering the simple of an
// anonymous class to be the empty string).
// Remove leading "\$[0-9]*" from the name
int length = simpleName.length();
if (length < 1 || simpleName.charAt(0) != '$')
throw new InternalError("Malformed class name");