Latest Posts

Archives [+]

Categories [+]

Authors [+]

Extending JCR with Scala

Recently I read Michael Marth's article Extending JCR with JRuby. Michael shows how to extend existing JCR interfaces with JRuby. He adds a method getAllNodes to JCR's Node interface. The getAllNodes method returns the nodes of the whole sub-tree of the current node. He further adds a method to Repository which makes using the getDescriptor method more convenient.

In this article I show how the same can be done with Scala. In contrast to JRuby, Scala is a statically typed language. Re-opening existing classes is not directly possible. However, using implicit definitions and the Pimp my Library pattern, a similar effect can be achieved while fully maintaining type safety.

First we add two functions to Node. The childNodes function returns a Scala Iterator over the direct child nodes of a node. This function is just a wrapper around the NodeIterator returned by getNodes. This is necessary since the allChildNodes function needs the richer functionality provided by Scala's iterators.

implicit def richNode(node: Node) = new RichNode(node)

class RichNode(node: Node) {
  
  def childNodes = new Iterator[Node] {
    val it = node.getNodes
    def hasNext = it.hasNext
    def next = it.nextNode
  }

  def allChildNodes: Iterator[Node] = {
    (for(n <- childNodes) 
       yield n.allChildNodes)
         .foldLeft[Iterator[Node]](childNodes)(_ ++ _)
  }

}

The for expression in allChildNodes recursively retrieves all child nodes of the direct child nodes of a node. The returned iterators are subsequently concatenated together by passing them to the foldLeft function together with the direct child nodes and the concatenation operator ++.

We can now use

for (n <- node.allChildNodes)
  println(n.getName)

to print the names of all nodes in a given node's sub-tree. When Scala cannot find the allChildNodes function on Node, it automatically converts the node to an instance of a RichNode as long as the implicit conversion function richNode is in scope.

We can now extend Repository in a similar way:

implicit def richRepository(repository: Repository) = new {
  def descriptor(name: String) = {
    if (repository.getDescriptor(name) == java.lang.Boolean.toString(true))
      true
    else if (repository.getDescriptor(name) == java.lang.Boolean.toString(false))
      false
    else 
       repository.getDescriptor(name)   
  }
}

Note the use of an anonymous class instead of explicitly declaring a RichRepository class. (Quiz: is it possible to use an anonymous class instead of RichNode in the first example too? How?)

We can now check for observation support like so:

if (repository.descriptor(Repository.OPTION_OBSERVATION_SUPPORTED) == true)
  println(Repository.OPTION_OBSERVATION_SUPPORTED)

Although it might look strange, explicitly comparing to true is necessary here. The reason is, that the return type of the descriptor function is not a Boolean but rather the least upper bound of the types in all branches of the if-else statement. The least upper bound for types Boolean and String is the type Any.

 
(optional)
No comments yet