Getting the Java out of your Scala

Jan Vermeir

To get some grip on the configuration of the Weblogic domains and servers at my current client, I created a tool that reads domain config files and translates them in a graph. I decided to solve this problem in Scala, mainly because I read about its powerful native XML parsing capabilities. Parsing XML turned out to be a total no-brainer, but I managed to learn something about how to solve problems the Scala way, so this is a story about Scala rather than parsing XML in Scala.

To solve my problem I had to parse the configuration file for a Weblogic domain, or more specifically (in my case) the file where Weblogic stores information about JMS resources. Parsing one of those files is done like this:

    val configData: Elem = XML.loadFile(jmsConfigFileName)

configData now holds a parsed version of the file whose name is stored in jmsConfigFileName. This file contains stuff like queue and topic definitions like the fragment below:

<queue name="queueTo">
    <sub-deployment-name>domain</sub-deployment-name>
    <jndi-name>domain/jms/qto</jndi-name>
  </queue>
  <topic name="domainJMSTopic">
    <sub-deployment-name>domain</sub-deployment-name>
    <jndi-name>domain/jms/topic</jndi-name>
  </topic>

My first attempt at retrieving all topic definitions from this config file was this:

  def findTopics(configData: Elem): Set[JmsObject] = {
    val jmsObjects = for (topic <- (configData \\ "topic"))
      yield (new JmsTopic((topic \ "@name").text, (topic \ "jndi-name").text))
    jmsObjects.toSet
  }

I felt really happy about having written this powerful and concise loop. Scala's for construct is powerful, as well as its XML parsing. To retrieve the queues I added the method below:

  def findQueues(configData: Elem): Set[JmsObject] = {
    val jmsObjects = for (queue <- (configData \\ "queue"))
      yield (new JmsQueue((queue \ "@name").text, (queue \ "jndi-name").text))
    jmsObjects.toSet
  }

The duplication was obvious, so I felt a little less happy with my solution but I couldn't see an easy way out. Enter my colleague and Scala trainer Urs Peter. He showed me two ways to improve the code.
Our first attempt looks like this:

def buildListOfJmsObjectsFromConfigData[T] 
           (configData:Elem, clazz:Class[T], startNode:String): Set[T] = {
    val jmsObjects = for (node <- (configData \\ startNode))
      yield ( clazz.getConstructors.apply(0).newInstance(
                             (node \ "@name").text
                           , (node \ "jndi-name").text).asInstanceOf[T])
    jmsObjects.toSet
  }

Which you can then call like this:

buildListOfJmsObjectsFromConfigData (configData, classOf[JmsConnectionFactory]
           , "connection-factory")

This looked quite Java-ish to me, as well as complex and obfuscated.

Our second attempt removes the loop and uses currying. It looks like this:

def buildListOfJmsObjectsFromConfigData[T]
            (configData:Elem, startNode:String) (f:(NodeSeq) => T): Set[T] = {
     (configData \\ startNode).map(f).toSet
}

This second variant of buildListOfJmsObjectsFromConfigData[T] can be called like this:

      buildListOfJmsObjectsFromConfigData (configData, "topic") {
        node => new JmsTopic((node \ "@name").text, (node \ "jndi-name").text)
      }

This works because the (configData \\ startNode) yields a set of objects on which we call map. Map takes a function as a parameter. The function shown above extracts a JmsTopic object, so we end up with a collection of JmsTopic instances.

I'm happy with our final version. It is concise and clear as well as extensible; way better than my first Java-ish version.

The code is attached here.
I created three versions that are exercised in a unit test. JmsConfigReaderV1 is the first Java-ish version, JmsConfigReaderV2 is the compact but obfuscated solution and JmsConfigReaderV3 is my final attempt.
I'm sure there are lots of opportunities to improve, so I welcome your opinions ans comments.

Comments (8)

  1. Amit Mujawar - Reply

    April 5, 2011 at 5:51 am

    Any language - programming as well as general 'language' has its own characteristics which gives flexibility to say same thing in different ways.
    Some people find conciseness a good feature .. at the same time it could be little awkward or cryptic for some guys.
    Having said that I also feel, it takes some time and practice to reach conciseness and use of exact words/semantics and leave the clutter away.
    Anyone who has used regular expressions would agree with me. The syntax is so small but u can achieve same functionality using n number of reg expressions. Yet each one has its own baggage of performance as well as expressiveness.
    I think the goal should not be just conciseness.

  2. Tamas - Reply

    April 5, 2011 at 8:07 am

    Hi,

    Did you measure the difference in R/T between the solutions?

    Cheers,

    T

  3. Oliver - Reply

    April 5, 2011 at 8:50 am

    The conciseness of your solution is mostly a result of making use of closures. Once Java has closures in JDK8, you will also be able to write much more concise code in Java. I'm not saying this as a counter argument to using Scala instead of Java or make use of some other impressing cool language. Rather than that it is more frustration I feel when I anticipate that closures in JDK8 will make a lot of Java people understand the power of closures, thus creating a closure hype (like earlier with byte code manipulation in Java). This will again create a lot of momentum for Java and yet another time the masses will jump on Java again and Scala, Ruby, Groovy, Clojure will remain niche players ... I only hope some companies out there will see it makes sense to hire people with an interest beyond writing Java boiler plate code.

  4. Jan Vermeir - Reply

    April 5, 2011 at 6:34 pm

    Hi Oliver, thanks for your comment. I guess you're right about closures being an important feature, and I hope it gets implemented well in Java8. I'm a little worried Java 8 will arrive late or not at all. On the other hand I'm currently working with Java5 on Weblogic 9.2, so where's the rush?
    There might be another reason to switch from Java to e.g. Scala or Closure. Java will remain (in my opinion) an OO language, while current language development tends toward Lisp derivatives. I read and can highly recommend, Bruce Tate's '7 languages in 7 weeks' (http://www.amazon.co.uk/Seven-Languages-Weeks-Programming-Programmers/dp/193435659X/ref=sr_1_1?s=books&ie=UTF8&qid=1302024421&sr=1-1) book. If only to shake up your brain and create some new neuro-connections.

  5. Fred - Reply

    April 5, 2011 at 7:02 pm

    View your source. Your site got hijacked.

  6. Erwin van der Koogh - Reply

    April 5, 2011 at 9:51 pm

    @Fred: You are right. Thanks for bringing it to our attention. Something fishy is going on. I am working on it.

  7. Gilles - Reply

    April 21, 2011 at 6:28 pm

    Wouldn't this:

    type Name=String
    type JndiName = String
    def buildListOfJmsObjectsFromConfigData[T]
    (configData:Elem, startNode:String) (f:(Name,JndiName) => T): Set[T] = {
    (configData \\ startNode).map {node =>
    val name = (node \ "@name").text
    val jndi = (node \ "jndi-name").text
    f(name,jndi)
    }.toSet
    }

    avoid some more redundancy? (Type aliases can help making sense of the closure parameters). Call-side verbosity is far more penalizing IMO.

    This would be used like:
    buildListOfJmsObjectsFromConfigData (configData, "topic") {(name,jndi) => new JmsTopic(name,jndi)}
    or even shorter:
    buildListOfJmsObjectsFromConfigData (configData, "topic") {new JmsTopic(_,_)}

  8. Jan Vermeir - Reply

    April 21, 2011 at 8:34 pm

    Hi Gilles, thanks for your comment, I'll give it a try and see what happens.

    You're right about the call-side-verbosity. Version 3 looks nice but the code to collect the different types of JMS object is the most verbose of the three. In real-life you may end up repeating lots of code like this, so reducing it's complexity and size seems important.

Add a Comment