0

I want to pass a scala file containing a case class so my application compiles this case class during run time and start using it.

The main reason why I am doing this is because I want to avoid rebuilding my code every time the case class changes. So would be better to pass it as a parameter (in case you are wondered, the operations with this case class are generic so it is not required any rework in the transformations)

I was using these post1, post2 and post3 as references. So far my application looks like this:

import scala.io.Source
import scala.reflect.runtime.universe
import scala.tools.reflect._

object TestCompile {
  def main(args: Array[String]): Unit = {

    val path = "C:\\myWorkspace\\entity\\TestClass.scala"
    val tb = universe.runtimeMirror(getClass.getClassLoader).mkToolBox()
    val src = Source.fromFile(path).mkString.stripMargin
    val clazz = tb.compile(tb.parse(src))().asInstanceOf[Class[_]]

  }
}

The file TestClass.scala is like this:

case class TestClass(
         val value : String,
         val timeStamp : Long,
         val rowKey : String,
         val columnFamily : String
)

But I am getting an exception in

val clazz = tb.compile(tb.parse(src))().asInstanceOf[Class[_]]

Exception:

Exception in thread "main" scala.tools.reflect.ToolBoxError: reflective compilation has failed: cannot initialize the compiler due to java.lang.VerifyError: scala/tools/reflect/ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$withCompilerApi$api$.liftedTree1$1(ToolBoxFactory.scala:344) at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$withCompilerApi$api$.compiler$lzycompute(ToolBoxFactory.scala:330) at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$withCompilerApi$api$.compiler(ToolBoxFactory.scala:329) at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$withCompilerApi$.liftedTree2$1(ToolBoxFactory.scala:356) at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$withCompilerApi$.apply(ToolBoxFactory.scala:354) at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl.parse(ToolBoxFactory.scala:413) at TestCompile$.main(App.scala:17) at TestCompile.main(App.scala) Caused by: java.lang.VerifyError: scala/tools/reflect/ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$withCompilerApi$api$.liftedTree1$1(ToolBoxFactory.scala:334)

Below is the dependency I am using, however I tried with other versions always getting the same error:

<dependency>
  <groupId>org.scala-lang</groupId>
  <artifactId>scala-reflect</artifactId>
  <version>2.11.6</version>
</dependency>

What am I doing wrong?

3 Answers 3

1

I can't reproduce VerifyError.

I have

java.lang.ClassCastException: scala.runtime.BoxedUnit cannot be cast to java.lang.Class

in the line val clazz = tb.compile(tb.parse(src))().asInstanceOf[Class[_]].

tb.compile(tb.parse(src)) has type () => Any, so tb.compile(tb.parse(src))() has type Any.

https://github.com/scala/scala/blob/2.13.x/src/compiler/scala/tools/reflect/ToolBox.scala#L129

Remove .asInstanceOf[Class[_]].

Also see Causes of getting a java.lang.VerifyError

Sign up to request clarification or add additional context in comments.

Comments

1

I got some time to test this today. The main problem I think is that the last expected line scala.reflect.classTag[TestClass].runtimeClass is missing in TestClass.scala.

One other important thing to remember is that scala-compiler.jar should also be in your class path.

Once I resolved that, I ran into another problem with Java/Scala object conversion. You have two options, make the timeStamp field, either a String or Java.lang.Long. Since the String conversion is trivial, I've given below example for Java.lang.Long.

With the above changes, my TestClass.scala looks like below:

case class TestClass(
                      value : String,
                      timeStamp : java.lang.Long,
                      rowKey : String,
                      columnFamily : String
                    ) {}

scala.reflect.classTag[TestClass].runtimeClass

I copied it to /tmp/ directory for testing.

Testing

My TestCompile.scala looks like below:

import scala.io.Source
import scala.reflect.runtime.universe
import scala.tools.reflect._

object TestCompile {
  def main(args: Array[String]): Unit = {

    val path = "/tmp/TestClass.scala"
    val tb = universe.runtimeMirror(getClass.getClassLoader).mkToolBox()
    val src = Source.fromFile(path).mkString.stripMargin
    println(src)
    val clazz = tb.compile(tb.parse(src))().asInstanceOf[Class[_]]

    val ctor = clazz.getDeclaredConstructors()(0)
    val instance = ctor.newInstance("My value", new java.lang.Long(1234567890L), "Row1", "Column1")
    println(instance.toString)

  }
}

Output

TestClass(My value,1234567890,Row1,Column1)

Comments

0

Because you lost dependency Add those dependency to your pom.xml

<dependency>
    <groupId>org.scala-lang</groupId>
    <artifactId>scala-library</artifactId>
    <version>2.11.8</version>
</dependency>
<dependency>
    <groupId>org.scala-lang</groupId>
    <artifactId>scala-reflect</artifactId>
    <version>2.11.8</version>
</dependency>
<dependency>
    <groupId>org.scala-lang</groupId>
    <artifactId>scala-compiler</artifactId>
    <version>2.11.8</version>
</dependency>

enter image description here

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.