vendredi 22 janvier 2016

Scala Reflection & TypeTag Mismatch

So I have a bunch of compiled case classes in a .jar. I want to load and iterate over all of them and for each case class generate an Avro schema using scalavro. The scalavro AvroType expects a TypeTag, so essentially my question is how to appropriately reflect TypeTags from external case classes in a jar.

import java.net.URL
import com.gensler.scalavro.types.AvroType
import org.clapper.classutil.ClassFinder
import java.io.File
import scala.reflect.internal.util.ScalaClassLoader.URLClassLoader
import scala.reflect.runtime.universe._
import scala.reflect.runtime.{ universe => ru }

object scalaAvroGen extends App {
  val jarLocation = "someCaseClasses.jar"
  val classpath = List(jarLocation).map(new File(_))
  val classLoader = new URLClassLoader(Array[URL](new File(jarLocation).toURI.toURL), this.getClass().getClassLoader())
  val finder = ClassFinder(classpath)
  val classes = finder.getClasses.filterNot(_.isFinal)
  classes.foreach {
    loadedClass => {
      val typeTag = typeToTypeTag(getType(classLoader.loadClass(loadedClass.name)))
      val avroSchema = AvroType.apply(typeTag).schema()
      println(avroSchema)
    }
  }

  def getType[T](clazz: Class[T])(implicit runtimeMirror: ru.Mirror) =
    runtimeMirror.classSymbol(clazz).toType

  def typeToTypeTag[T](tpe: Type): TypeTag[T] = TypeTag.synchronized {
    val mirror = scala.reflect.runtime.currentMirror
    TypeTag(mirror, new reflect.api.TypeCreator {
      def apply[U <: reflect.api.Universe with Singleton](m: reflect.api.Mirror[U]) = {
        assert(m eq mirror, s"TypeTag[$tpe] defined in $mirror cannot be migrated to $m.")
        tpe.asInstanceOf[U#Type]
      }
    })
  }
}

Example case class in the compiled .jar:

case class SimpleScalaAvroObject(version: Int, name: String)

Currently when I attempt to run this code I get the following error:

Error:(20, 39) type mismatch;  found   : reflect.runtime.universe.TypeTag[Nothing]  required: reflect.runtime.universe.TypeTag[T] Note: Nothing <: T, but trait TypeTag is invariant in type T. You may wish to investigate a wildcard type such as `_ <: T`. (SLS 3.2.10)
      val avroSchema = AvroType.apply(typeTag).schema()    
                              ^

Now I'm sure this is not my only issue, I've spent the last two days throwing everything at this. At one point I was getting class not found exceptions on SimpleScalaAvroObject so I'm probably not even reflecting correctly.

I'll eventually annotations to the case classes I actually care about but for now this is really just a PoC.

Please note up until a few months ago I was a C# dev so sorry for this mangled mess of Scala.

Thanks!





Aucun commentaire:

Enregistrer un commentaire