Over a million developers have joined DZone.

Scala Reflection Basics – Accessing an Object's Private Field Explained

· Java Zone

Microservices! They are everywhere, or at least, the term is. When should you use a microservice architecture? What factors should be considered when making that decision? Do the benefits outweigh the costs? Why is everyone so excited about them, anyway?  Brought to you in partnership with IBM.

Today’s blog post will be on the slightly weird side, because as Scala developers we don’t usually need to reach into reflection to solve problems at hand. It’s easy to fight most problems with the “there’s a monad for that!” tactic, or simply “adding yet another layer solves any software development problem!”.

Sadly today’s task is not easily solvable by just that. The problem we’re going to tackle using Scala’s reflection involves a scala object and a private field we need to access.

trait MongoFlatSpec extends FlatSpec with BeforeAndAfterAll {
  var mongo: Mongo = _ // either Mongo or Fongo supplied instance here, starts up during beforeAll()
  abstract override protected def afterAll() {
    super.afterAll()
    // now it's tempting to use 
    MongoDB.close() // NOPE! Won't allow parallel execution, take a look at it's impl above.
    getMongo.close()
  }
}
class ExampleTest extends MongoFlatSpec with ShouldMatchers {
  // tests...
}

Problem statement: We need to support parallel integration tests (sbt running tests in parallel), using Mongo (or Fongo). The MongoDB object though, contains globaly shared mutable state, in form of the dbs Map, which is used to determine which MongoAddressto hit for which entity.

We are able to setup the mongo identifiers up properly for the threads executing the tests, so they don’t interfere with each other, and we won’t focus on the setup part today. Instead let’s focus on what happens during teardown. A naive implementation is a simple (ScalaTest) afterAll, like this:

trait MongoFlatSpec extends FlatSpec with BeforeAndAfterAll {
  
  var mongo: Mongo = _ // either Mongo or Fongo supplied instance here, starts up during beforeAll()
  
  abstract override protected def afterAll() {
    super.afterAll()
 
    // now it's tempting to use 
    MongoDB.close() // NOPE! Won't allow parallel execution, take a look at it's impl above.
 
    getMongo.close()
  }
}
 
class ExampleTest extends MongoFlatSpec with ShouldMatchers {
  // tests...
}

So sadly the naive implementation with using MongoDB.close() is not enough for us. Why? Imagine Two threads, running two MongoFlatSpecs, assume each has it’s own in memory Fongo instance even. There still is that shared MongoDB.dbs field deep in lift-mongodb’s internals. If we’d use MongoDB.close() we’ll clean all MongoIdentifiers, so we might break tests which are still in flight… We could try something among the lines of “wait for the right moment to clean up”, but … why wait!? There’s a ConcurrentHashMap in there, and we just want to remove “the one MongoIdentifier I have been using for this MongoFlatSpec”.

*Reflection to the rescue!* So we’ll have to use reflection, in order to execute such statement: MongoDB.dbs.remove(myMongoIdentifier). The first problem is that we’re dealing with a Scala object, also known as “Module”. Why is it also known as *Module*? Let’s take a look into the ByteCode!

scala> :javap MongoDB
public class MongoDB$ extends java.lang.Object {
    public static final MongoDB$ MODULE$; // actual instance of our `object`
    public static {};                     
    public MongoDB$();
    // ...
}
Since we want the dbs field, we will have to go through the static final MongoDB$ MODULE$ field of the MongoDB$ class. While it’s certainly doable using plain Java reflection, let’s see how Scala’s (still experimental) reflection can make this a bit nicer:
def removeFongoIdentifierFromMongoDBObject(ident: MongoIdentifier) {
  val ru = scala.reflect.runtime.universe
  val mirror = ru.runtimeMirror(getClass.getClassLoader)
  type MongosMap = ConcurrentHashMap[MongoIdentifier, MongoAddress]
  val mongoModuleSymbol = ru.typeOf[MongoDB.type].termSymbol.asModule
  val moduleMirror = mirror.reflectModule(mongoModuleSymbol)
  val instanceMirror = mirror.reflect(moduleMirror.instance)
  val dbsTerm = ru.typeOf[MongoDB.type].declaration(ru.newTermName("dbs")).asTerm.accessed.asTerm
  val fieldMirror = instanceMirror.reflectField(dbsTerm)
  val mongosMap = fieldMirror.get.asInstanceOf[MongosMap]
  mongosMap remove ident
}
The first thing to notice here is the use of Mirrors instead of explicitly callinggetDeclaredField and it’s friends on Class<?> objects like you would in plain Java APIs. Why the hassle? The grand idea behind Mirrors (other than “it sounds so cool – reflection => mirrors, yay!“) is that you can obtain either a runtime or macrosuniverse. This is discussed in detail in the environment-universes-mirrors section of the Scala docs if you want to dig deeper. The quick overview though is this: when you think about it, AST and reflection stuff are pretty similar. I mean, we traverse the same things, only at a different time (compile-time vs. runtime), so some things may be not available in the runtime, but why change the entire API if we could find some common ground – that is, Mirrors. Sure, they will be a bit different if used from the macros universe than from the runtime universe, but as we also write compile time macros the benefit is huge – only one API with slight flavoring to learn, instead of two separate ones – depending on the stage of when we apply them.

Coming back to our code though, as you can see, we were able to avoid going through the magic MODULE$ constant explicitly, it was abstracted away from us thanks to the types of mirrors we’ve been using – it’s also worth noting that for example thereflectModule(ModuleSymbol) takes a ModuleSymbol which is obtained via the “safe cast”, as one might think of it in the line above using the asModule method. The nice thing about asModule is that it will fail if the term you’re calling it on is not a module. So the API is both designed to be as typesafe as possible, even in reflection-lang, as well as “fail fast” if it detects you’re doing something wrong.

For more docs or further reference check out the docs about scala-reflection or ScalaDoc about scala.reflect.api.Mirrors.

Discover how the Watson team is further developing SDKs in Java, Node.js, Python, iOS, and Android to access these services and make programming easy. Brought to you in partnership with IBM.

Topics:

Published at DZone with permission of Konrad Malawski, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}