【问题标题】:How to eval code that uses InterfaceStability annotation (that fails with "illegal cyclic reference involving class InterfaceStability")?如何评估使用 InterfaceStability 注释的代码(因“涉及类 InterfaceStability 的非法循环引用而失败”)?
【发布时间】:2019-05-27 07:48:23
【问题描述】:

我想在程序运行的时候动态生成一些kafka流代码,但是用scala工具箱eval编译如下kafka流代码时出现异常:

val toolbox = runtimeMirror(getClass.getClassLoader).mkToolBox()
val code =
  """
    |import org.apache.kafka.streams.scala.kstream.KStream
    |import org.apache.kafka.streams.scala.StreamsBuilder
    |
    |class ClassA {
    |  def createStream(): KStream[String, String] = {
    |    import org.apache.kafka.streams.scala.ImplicitConversions._
    |    import org.apache.kafka.streams.scala.Serdes._
    |    new StreamsBuilder().stream("TestTopic")
    |  }
    |}
    |scala.reflect.classTag[ClassA].runtimeClass
  """.stripMargin
toolbox.eval(toolbox.parse(code))

错误:

reflective compilation has failed:

illegal cyclic reference involving class InterfaceStability
scala.tools.reflect.ToolBoxError: reflective compilation has failed:

illegal cyclic reference involving class InterfaceStability
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal.throwIfErrors(ToolBoxFactory.scala:331)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal.wrapInPackageAndCompile(ToolBoxFactory.scala:213)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal.compile(ToolBoxFactory.scala:267)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl.$anonfun$compile$13(ToolBoxFactory.scala:444)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$withCompilerApi$.apply(ToolBoxFactory.scala:370)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl.compile(ToolBoxFactory.scala:437)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl.eval(ToolBoxFactory.scala:459)

好像kafka的InterfaceStability类自己注解了:

package org.apache.kafka.common.annotation;

@InterfaceStability.Evolving
public class InterfaceStability {
...
}

Scala 版本:"2.12.8"

kafkaVersion:“2.1.0”

Sbt 版本:“1.2.7”

【问题讨论】:

    标签: scala reflection apache-kafka apache-kafka-streams


    【解决方案1】:

    切换到scala.tools.nsc包下面的api后,就可以编译成功了。

    参考https://eknet.org/main/dev/runtimecompilescala.html[断开链接]

    import scala.reflect.internal.util.{AbstractFileClassLoader, BatchSourceFile}
    import scala.reflect.io.{AbstractFile, VirtualDirectory}
    import scala.reflect.runtime
    import scala.reflect.runtime.universe
    import scala.reflect.runtime.universe._
    import scala.tools.nsc.{Global, Settings}
    import scala.collection.mutable
    import java.security.MessageDigest
    import java.math.BigInteger
    
    class Compiler(targetDir: Option[File]) {
    
      val target = targetDir match {
        case Some(dir) => AbstractFile.getDirectory(dir)
        case None => new VirtualDirectory("(memory)", None)
      }
    
      val classCache = mutable.Map[String, Class[_]]()
    
      private val settings = new Settings()
      settings.deprecation.value = true // enable detailed deprecation warnings
      settings.unchecked.value = true // enable detailed unchecked warnings
      settings.outputDirs.setSingleOutput(target)
      settings.usejavacp.value = true
    
      private val global = new Global(settings)
      private lazy val run = new global.Run
    
      val classLoader = new AbstractFileClassLoader(target, this.getClass.getClassLoader)
    
      /**Compiles the code as a class into the class loader of this compiler.
       *
       * @param code
       * @return
       */
      def compile(code: String) = {
        val className = classNameForCode(code)
        findClass(className).getOrElse {
          val sourceFiles = List(new BatchSourceFile("(inline)", wrapCodeInClass(className, code)))
          run.compileSources(sourceFiles)
          findClass(className).get
        }
      }
    
      /** Compiles the source string into the class loader and
       * evaluates it.
       *
       * @param code
       * @tparam T
       * @return
       */
      def eval[T](code: String): T = {
        val cls = compile(code)
        cls.getConstructor().newInstance().asInstanceOf[() => Any].apply().asInstanceOf[T]
      }
    
      def findClass(className: String): Option[Class[_]] = {
        synchronized {
          classCache.get(className).orElse {
            try {
              val cls = classLoader.loadClass(className)
              classCache(className) = cls
              Some(cls)
            } catch {
              case e: ClassNotFoundException => None
            }
          }
        }
      }
    
      protected def classNameForCode(code: String): String = {
        val digest = MessageDigest.getInstance("SHA-1").digest(code.getBytes)
        "sha"+new BigInteger(1, digest).toString(16)
      }
    
      /*
      * Wrap source code in a new class with an apply method.
      */
      private def wrapCodeInClass(className: String, code: String) = {
        "class " + className + " extends (() => Any) {\n" +
          "  def apply() = {\n" +
          code + "\n" +
          "  }\n" +
          "}\n"
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-18
      • 2017-09-04
      • 1970-01-01
      • 1970-01-01
      • 2019-05-31
      相关资源
      最近更新 更多