繁体   English   中英

SBT 在尝试运行 spark 时给出 java.lang.NullPointerException

[英]SBT gives java.lang.NullPointerException when trying to run spark

我正在尝试在系统为 CentOs6 的 Linux 机器上使用 sbt 1.7.2 编译 spark。

当我尝试运行 clean 命令时:./build/ ./build/sbt clean

我得到以下输出:

java.lang.NullPointerException
    at sun.net.util.URLUtil.urlNoFragString(URLUtil.java:50)
    at sun.misc.URLClassPath.getLoader(URLClassPath.java:526)
    at sun.misc.URLClassPath.getNextLoader(URLClassPath.java:498)
    at sun.misc.URLClassPath.getResource(URLClassPath.java:252)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:363)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:362)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:419)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:406)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:406)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:406)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:406)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:352)
    at sbt.internal.XMainConfiguration.run(XMainConfiguration.java:51)
    at sbt.xMain.run(Main.scala:46)
    at xsbt.boot.Launch$.$anonfun$run$1(Launch.scala:149)
    at xsbt.boot.Launch$.withContextLoader(Launch.scala:176)
    at xsbt.boot.Launch$.run(Launch.scala:149)
    at xsbt.boot.Launch$.$anonfun$apply$1(Launch.scala:44)
    at xsbt.boot.Launch$.launch(Launch.scala:159)
    at xsbt.boot.Launch$.apply(Launch.scala:44)
    at xsbt.boot.Launch$.apply(Launch.scala:21)
    at xsbt.boot.Boot$.runImpl(Boot.scala:78)
    at xsbt.boot.Boot$.run(Boot.scala:73)
    at xsbt.boot.Boot$.main(Boot.scala:21)
    at xsbt.boot.Boot.main(Boot.scala)
[error] [launcher] error during sbt launcher: java.lang.NullPointerException

当我使用 sbt 1.7.3 时也发生了,但是当我使用 sbt 1.6.2 时它可以成功清理和编译 spark。

我应该先检查什么? 我真的很感激任何人可以提供的任何建议。

一些关于如何调试 Spark 和 sbt 的建议。

如何在 IntelliJ 中构建 Spark。

克隆https://github.com/apache/spark ,在 IntelliJ 中打开它作为 sbt 项目。

在我可以在 IntelliJ 中运行我的代码之前,我必须执行sbt compile并重新打开项目(之前我有一个错误object SqlBaseParser is not a member of package org.apache.spark.sql.catalyst.parser )。 例如,我可以将以下对象放入sql/core/src/main/scala并在 IntelliJ 中运行/调试它

// scalastyle:off
import org.apache.spark.sql.{Dataset, SparkSession}

object MyMain extends App {
  val spark = SparkSession.builder()
    .master("local")
    .appName("SparkTestApp")
    .getOrCreate()

  case class Person(id: Long, name: String)

  import spark.implicits._

  val df: Dataset[Person] = spark.range(10).map(i => Person(i, i.toString))

  df.show()

//+---+----+
//| id|name|
//+---+----+
//|  0|   0|
//|  1|   1|
//|  2|   2|
//|  3|   3|
//|  4|   4|
//|  5|   5|
//|  6|   6|
//|  7|   7|
//|  8|   8|
//|  9|   9|
//+---+----+

}

当这些弹出窗口出现时,我还按下了Run npm installLoad Maven project ,但我没有注意到其中的区别。

还有一次我不得不在sql/catalyst/target/scala-2.12/src_managed中保留Project Structure只有一个源根sql/catalyst/target/scala-2.12/src_managed/main (而不是sql/catalyst/target/scala-2.12/src_managed/main/antlr4 )。 我有错误,例如SqlBaseLexer is already defined as class SqlBaseLexer

使用 IntelliJ IDEA 构建 Apache Spark 源代码: https ://yujheli-wordpress-com.translate.goog/2020/03/26/build-apache-spark-source-code-with-intellij-idea/?_x_tr_sl=auto&_x_tr_tl= en&_x_tr_hl=uk&_x_tr_pto=wapp (中文原文: https ://yujheli.wordpress.com/2020/03/26/build-apache-spark-source-code-with-intellij-idea/)

为什么构建 Spark 源会给出“object sbt is not a member of package com.typesafe”?

如何在 IntelliJ 中构建 sbt。

sbt 本身很棘手https://www.lihaoyi.com/post/SowhatswrongwithSBT.html并且构建它也有点棘手。

克隆https://github.com/sbt/sbt ,在 IntelliJ 中打开它。 让我们尝试使用这个克隆的 sbt 运行之前的 Spark 代码。

sbt 似乎不打算在指定目录中运行。 我将以下对象放在client/src/main/scala

object MyClient extends App {
  System.setProperty("user.dir", "../spark")
  sbt.client.Client.main(Array("sql/runMain MyMain"))
}

(通常,不建议改变系统属性user.dirHow to use "cd" command using Java runtime?

我必须首先执行sbt compile (这包括命令sbt generateContrabands --- sbt 使用 sbt 插件sbt-contrabandContrabandPluginJsonCodecPlugin ),以前是sbt-datatype ,用于代码生成: https ://github.com/sbt/contraband https://www.scala-sbt.org/contraband/ https://www.scala-sbt.org/1.x/docs/Datatype.html https://github.com/eed3si9n/gigahorse/tree/develop /core/src/main/违禁品)。 我有错误not found: value ScalaKeywords

下一个错误是type ExcludeItem is not a member of package sbt.internal.bsp 您可以在protocol/src/main/contraband-scala/sbt/internal/bsp/codec中删除文件ExcludeItemFormats.scalaExcludesItemFormats.scalaExcludesParamsFormats.scalaExcludesResultFormats.scala 它们是过时的自动生成文件。 您可以检查是否删除目录protocol/src/main/contraband-scala的内容(这是自动生成源的根)并执行sbt generateContrabands除了这四个文件之外的所有文件都将被恢复。 由于某些原因,这些文件并没有混淆 sbt,而是混淆了 IntelliJ。

现在,在运行时, MyClient产生

//[info] +---+----+
//[info] | id|name|
//[info] +---+----+
//[info] |  0|   0|
//[info] |  1|   1|
//[info] |  2|   2|
//[info] |  3|   3|
//[info] |  4|   4|
//[info] |  5|   5|
//[info] |  6|   6|
//[info] |  7|   7|
//[info] |  8|   8|
//[info] |  9|   9|
//[info] +---+----+

sbt.client.Client被称为瘦客户端。 或者,您可以在本地发布它并作为依赖项使用

build.sbt ( https://github.com/sbt/sbt/blob/v1.8.0/build.sbt#L1160 )

lazy val sbtClientProj = (project in file("client"))
  .enablePlugins(NativeImagePlugin)
  .dependsOn(commandProj)
  .settings(
    commonBaseSettings,
    scalaVersion := "2.12.11",
    publish / skip := false, // change true to false
    name := "sbt-client",
    .......

sbt publishLocal

一个新项目:

构建.sbt

scalaVersion := "2.12.17"

// ~/.ivy2/local/org.scala-sbt/sbt-client/1.8.1-SNAPSHOT/jars/sbt-client.jar
libraryDependencies += "org.scala-sbt" % "sbt-client" % "1.8.1-SNAPSHOT"

源代码/main/scala/Main.scala

object Main extends App {
  System.setProperty("user.dir", "../spark")
  sbt.client.Client.main(Array("sql/runMain MyMain"))
  
  //[info] +---+----+
  //[info] | id|name|
  //[info] +---+----+
  //[info] |  0|   0|
  //[info] |  1|   1|
  //[info] |  2|   2|
  //[info] |  3|   3|
  //[info] |  4|   4|
  //[info] |  5|   5|
  //[info] |  6|   6|
  //[info] |  7|   7|
  //[info] |  8|   8|
  //[info] |  9|   9|
  //[info] +---+----+
}

但是瘦客户端不是 sbt 正常运行的方式。 堆栈跟踪中的sbt.xMain来自https://github.com/sbt/sbt 它在这里: https ://github.com/sbt/sbt/blob/1.8.x/main/src/main/scala/sbt/Main.scala#L44 但是来自堆栈跟踪的xsbt.boot.Boot不是来自这个repo,它来自https://github.com/sbt/launcher ,即https://github.com/sbt/launcher/blob/1.x/launcher-implementation/src/main/scala/xsbt/boot/Boot .scala

问题是 sbt 分两步运行。 sbt 可执行文件(通常从https://www.scala-sbt.org/download.html#universal-packages下载)是一个 shell 脚本,首先它运行sbt-launch.jar (对象xsbt.boot.Boot

https://github.com/sbt/sbt/blob/v1.8.0/sbt#L507-L512

execRunner "$java_cmd" \
  "${java_args[@]}" \
  "${sbt_options[@]}" \
  -jar "$sbt_jar" \
  "${sbt_commands[@]}" \
  "${residual_args[@]}"

其次,后者反射性地调用 sbt(类sbt.xMain

https://github.com/sbt/launcher/blob/v1.4.1/launcher-implementation/src/main/scala/xsbt/boot/Launch.scala#L147-L149

val main = appProvider.newMain()
try {
  withContextLoader(appProvider.loader)(main.run(appConfig))

https://github.com/sbt/launcher/blob/v1.4.1/launcher-implementation/src/main/scala/xsbt/boot/Launch.scala#L496

// implementation of the above appProvider.newMain()
else if (AppMainClass.isAssignableFrom(entryPoint)) mainClass.newInstance

https://github.com/sbt/launcher/blob/v1.4.1/launcher-implementation/src/main/scala/xsbt/boot/PlainApplication.scala#L13

// implementation of the above main.run(appConfig)
mainMethod.invoke(null, configuration.arguments).asInstanceOf[xsbti.Exit]

然后xMain#run通过XMainConfiguration#run反射调用xMain.run

https://github.com/sbt/sbt/blob/v1.8.0/main/src/main/scala/sbt/Main.scala#L44-L47

class xMain extends xsbti.AppMain {
  def run(configuration: xsbti.AppConfiguration): xsbti.MainResult =
    new XMainConfiguration().run("xMain", configuration)
}

https://github.com/sbt/sbt/blob/v1.8.0/main/src/main/java/sbt/internal/XMainConfiguration.java#L51-L57

Class<?> clazz = loader.loadClass("sbt." + moduleName + "$");
Object instance = clazz.getField("MODULE$").get(null);
Method runMethod = clazz.getMethod("run", xsbti.AppConfiguration.class);
try {
  .....
  return (xsbti.MainResult) runMethod.invoke(instance, updatedConfiguration);

然后它下载并运行必要版本的 Scala(在build.sbt中指定)和其余 sbt 的必要版本(在project/build.properties中指定)。

启动器是什么。

让我们为启动器考虑一个 helloworld。

启动器由一个库(接口)组成

https://mvnrepository.com/artifact/org.scala-sbt/launcher-interface

https://github.com/sbt/launcher/tree/1.x/launcher-interface

和启动器可运行的罐子

https://mvnrepository.com/artifact/org.scala-sbt/launcher

https://github.com/sbt/launcher/tree/1.x/launcher-implementation/src

创建一个项目(取决于编译时的启动器界面)

构建.sbt

lazy val root = (project in file("."))
  .settings(
    name := "scalademo",
    organization := "com.example",
    version := "0.1.0-SNAPSHOT",
    scalaVersion := "2.13.10",
    libraryDependencies ++= Seq(
      "org.scala-sbt" % "launcher-interface" % "1.4.1" % Provided,
    ),
  )

src/main/scala/mypackage/Main.scala (此类将是使用启动器时的入口点)

package mypackage

import xsbti.{AppConfiguration, AppMain, Exit, MainResult}

class Main extends AppMain {
  def run(configuration: AppConfiguration): MainResult = {
    val scalaVersion = configuration.provider.scalaProvider.version

    println(s"Hello, World! Running Scala $scalaVersion")
    configuration.arguments.foreach(println)

    new Exit {
      override val code: Int = 0
    }
  }
}

sbt publishLocal 项目 jar 将发布在~/.ivy2/local/com.example/scalademo_2.13/0.1.0-SNAPSHOT/jars/scalademo_2.13.jar

下载启动器可运行 jar https://repo1.maven.org/maven2/org/scala-sbt/launcher/1.4.1/launcher-1.4.1.jar

创建启动器配置

我的应用程序配置

[scala]
  version: 2.13.10
[app]
  org: com.example
  name: scalademo
  version: 0.1.0-SNAPSHOT
  class: mypackage.Main
  cross-versioned: binary
[repositories]
  local
  maven-central
[boot]
  directory: ${user.home}/.myapp/boot

然后命令java -jar launcher-1.4.1.jar @my.app.configuration abc产生

//Hello world! Running Scala 2.13.10
//a
//b
//c

出现了文件

~/.myapp/boot/scala-2.13.10/com.example/scalademo/0.1.0-SNAPSHOT
  scalademo_2.13.jar
  scala-library-2.13.10.jar
~/.myapp/boot/scala-2.13.10/lib
  java-diff-utils-4.12.jar
  jna-5.9.0.jar
  jline-3.21.0.jar
  scala-library.jar
  scala-compiler.jar
  scala-reflect.jar

因此启动器有助于在仅安装 Java 的环境中运行应用程序(不需要 Scala),将使用 Ivy 依赖项解析。 有一些功能可以处理返回码、使用不同的 Scala 版本重启应用程序、启动服务器等。

或者,可以使用以下任何命令

java -Dsbt.boot.properties=my.app.configuration -jar launcher-1.4.1.jar
java -jar launcher-repacked.jar      # put my.app.configuration to sbt/sbt.boot.properties/ and repack the jar

https://www.scala-sbt.org/1.x/docs/Launcher-Getting-Started.html

如何使用启动器运行 sbt。

Sbt https://github.com/sbt/sbt使用 sbt 插件SbtLauncherPlugin https://github.com/sbt/sbt/blob/v1.8.0/project/SbtLauncherPlugin.scala以便从原始启动器启动launcher

https://github.com/sbt/launcher/tree/1.x/launcher-implementation/src

https://mvnrepository.com/artifact/org.scala-sbt/launcher

它构建sbt-launch

https://github.com/sbt/sbt/tree/v1.8.0/launch

https://mvnrepository.com/artifact/org.scala-sbt/sbt-launch

基本上, sbt-launchlauncher的不同之处在于注入了默认配置sbt.boot.properties

如果我们想用启动器运行 sbt 那么我们应该找到一种方法来为 sbt 指定一个工作目录(类似于我们在使用瘦客户端时的做法)。

工作目录可以设置为 1) 在sbt.xMain ( sbt ) 中或 2) 在xsbt.boot.Boot ( sbt-launcher ) 中。

1)使sbt.xMain非最终的,以便它可以被扩展

/*final*/ class xMain extends xsbti.AppMain { 
...........

https://github.com/sbt/sbt/blob/v1.8.0/main/src/main/scala/sbt/Main.scala#L44

将新类放入main/src/main/scala (启动器样式的入口点)

import sbt.xMain
import xsbti.{ AppConfiguration, AppProvider, MainResult }
import java.io.File

class MyXMain extends xMain {
  override def run(configuration: AppConfiguration): MainResult = {
    val args = configuration.arguments

    val (dir, rest) =
      if (args.length >= 1 && args(0).startsWith("dir=")) {
        (
          Some(args(0).stripPrefix("dir=")),
          args.drop(1)
        )
      } else {
        (None, args)
      }

    dir.foreach { dir =>
      System.setProperty("user.dir", dir)
    }

    // xMain.run(new AppConfiguration { // not ok
    // new xMain().run(new AppConfiguration { // not ok
    super[xMain].run(new AppConfiguration { // ok
      override val arguments: Array[String] = rest
      override val baseDirectory: File =
        dir.map(new File(_)).getOrElse(configuration.baseDirectory)
      override val provider: AppProvider = configuration.provider
    })
  }
}

sbt publishLocal

我的.sbt.configuration

[scala]
  version: auto
  #version: 2.12.17
[app]
  org: org.scala-sbt
  name: sbt
  #name: main  # not ok
  version: 1.8.1-SNAPSHOT
  class: MyXMain
  #class: sbt.xMain
  components: xsbti,extra
  cross-versioned: false
  #cross-versioned: binary
[repositories]
  local
  maven-central
[boot]
  directory: ${user.home}/.mysbt/boot
[ivy]
  ivy-home: ${user.home}/.ivy2

一个命令:

java -jar launcher-1.4.1.jar @my.sbt.configuration dir=/path_to_spark/spark "sql/runMain MyMain"

要么

java -jar sbt-launch.jar @my.sbt.configuration dir=/path_to_spark/spark "sql/runMain MyMain"

//[info] +---+----+
//[info] | id|name|
//[info] +---+----+
//[info] |  0|   0|
//[info] |  1|   1|
//[info] |  2|   2|
//[info] |  3|   3|
//[info] |  4|   4|
//[info] |  5|   5|
//[info] |  6|   6|
//[info] |  7|   7|
//[info] |  8|   8|
//[info] |  9|   9|
//[info] +---+----+

sbt-launch.jar取自~/.ivy2/local/org.scala-sbt/sbt-launch/1.8.1-SNAPSHOT/jars或只是https://mvnrepository.com/artifact/org.scala-sbt /sbt-launch因为我们还没有修改启动器)

我必须从spark复制scalastyle-config.xml ,否则找不到。

我仍然有fatal: Not a git repository (or any parent up to mount parent...) Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).

2)

项目/Dependencies.scala ( https://github.com/sbt/sbt/blob/v1.8.0/project/Dependencies.scala#L25 )

val launcherVersion = "1.4.2-SNAPSHOT" // modified

克隆https://github.com/sbt/launcher并进行以下更改

build.sbt ( https://github.com/sbt/launcher/blob/v1.4.1/build.sbt#L11 )

ThisBuild / version := {
  val orig = (ThisBuild / version).value
  if (orig.endsWith("-SNAPSHOT")) "1.4.2-SNAPSHOT" // modified
  else orig
}

启动器实现/src/main/scala/xsbt/boot/Launch.scala ( https://github.com/sbt/launcher/blob/v1.4.1/launcher-implementation/src/main/scala/xsbt/boot/启动.scala#L17 #L21 )

class LauncherArguments(
    val args: List[String],
    val isLocate: Boolean,
    val isExportRt: Boolean,
    val dir: Option[String] = None // added
)

object Launch {
  def apply(arguments: LauncherArguments): Option[Int] =
    apply((new File(arguments.dir.getOrElse(""))).getAbsoluteFile, arguments) // modified

  .............

启动器实现/src/main/scala/xsbt/boot/Boot.scala ( https://github.com/sbt/launcher/blob/v1.4.1/launcher-implementation/src/main/scala/xsbt/boot/启动.scala#L41-L67 )

  def parseArgs(args: Array[String]): LauncherArguments = {
    @annotation.tailrec
    def parse(
        args: List[String],
        isLocate: Boolean,
        isExportRt: Boolean,
        remaining: List[String],
        dir: Option[String] // added
    ): LauncherArguments =
      args match {
        ...................
        case "--locate" :: rest        => parse(rest, true, isExportRt, remaining, dir) // modified
        case "--export-rt" :: rest     => parse(rest, isLocate, true, remaining, dir) // modified

          // added
        case "--mydir" :: next :: rest => parse(rest, isLocate, isExportRt, remaining, Some(next))

        case next :: rest              => parse(rest, isLocate, isExportRt, next :: remaining, dir) // modified
        case Nil                       => new LauncherArguments(remaining.reverse, isLocate, isExportRt, dir) // modified
      }
    parse(args.toList, false, false, Nil, None)
  }

sbt 启动器: sbt publishLocal

sbt: sbt publishLocal

我的.sbt.configuration

[scala]
  version: auto
[app]
  org: org.scala-sbt
  name: sbt
  version: 1.8.1-SNAPSHOT
  #class: MyXMain
  class: sbt.xMain
  components: xsbti,extra
  cross-versioned: false
[repositories]
  local
  maven-central
[boot]
  directory: ${user.home}/.mysbt/boot
[ivy]
  ivy-home: ${user.home}/.ivy2

一个命令:

java -jar launcher-1.4.2-SNAPSHOT.jar @my.sbt.configuration --mydir /path_to_spark/spark "sql/runMain MyMain"

要么

java -jar sbt-launch.jar @my.sbt.configuration --mydir /path_to_spark/spark "sql/runMain MyMain"

要么

java -jar sbt-launch.jar --mydir /path_to_spark/spark "sql/runMain MyMain" (使用默认sbt.boot.properties而不是my.sbt.configuration

(我们正在使用修改后的launcher或使用此修改后的launcher器的新sbt-launch )。

或者,我们可以在 IntelliJ 中的xsbt.boot.Boot的“运行配置”中指定“程序参数”

@/path_to_sbt_config/my.sbt.configuration --mydir /path_to_spark/spark "sql/runMain MyMain"

也可以在 IntelliJ 的“运行配置”中指定工作目录/path_to_spark/spark 然后剩下的“程序参数”是

@/path_to_sbt_config/my.sbt.configuration "sql/runMain MyMain"

我尝试使用"org.scala-sbt" % "launcher" % "1.4.2-SNAPSHOT""org.scala-sbt" % "sbt-launch" % "1.8.1-SNAPSHOT"作为依赖但得到了No RuntimeVisibleAnnotations in classfile with ScalaSignature attribute: class Boot

你的设置。

因此,我们可以在 IntelliJ 中和/或使用println运行/调试 sbt-launcher 代码,并使用println运行/调试 sbt 代码(因为没有可运行的对象)。

从您的堆栈跟踪中我怀疑其中一个类加载器urls为 null

https://github.com/openjdk/jdk/blob/jdk8-b120/jdk/src/share/classes/sun/misc/URLClassPath.java#L82

也许你可以添加到sbt.xMain#runMyXMain#run类的东西

var cl = getClass.getClassLoader
while (cl != null) {
  println(s"classloader: ${cl.getClass.getName}")
  cl match {
    case cl: URLClassLoader =>
      println("classloader urls:")
      cl.getURLs.foreach(println)
    case _ =>
      println("not URLClassLoader")
  }
  cl = cl.getParent
}

为了查看什么 url 为空。

https://www.scala-sbt.org/1.x/docs/Developers-Guide.html

https://github.com/sbt/sbt/blob/1.8.x/DEVELOPING.md

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM