The previous posts have all used Scala case classes to implement the illustrative Domain objects. The key attribute is their immutability, which allows to ignore where and how they are referenced. A production implementation will be brown field have to deal with existing Java code, which will not implement immutability. The signature JavaBean is explicitly mutable! The wide usage of XML means references to org.w3c.dom.Document, whose implementation is definitely not thread safe.
One might convert case classes to/from the external mutable implementations. For example, Camel's
translators could be added to the routes. The costs of such translators could be unacceptable (in both development and runtime).
The mutable data could be hidden inside a wrapper class that only allows access under a monitor, much like Collections.synchronized*.
A particular service often uses only a very small subset of the available functionality. That makes it sensible to have the wrapper accept functions to manipulate the object and return immutable results.
trait Monitor[T] {
def apply[R](f: T => R): R
}
object Monitor {
implicit def apply[S, T](s: S)(implicit p: S => T) = new MonitorImpl(p(s))
private[Monitor] class MonitorImpl[T](private val v: T) extends Monitor[T] {
def apply[R](f: T => R) = synchronized { f(v) }
}
}
For example
def content(d: Document) = d.getDocumentElement.getTextContent
implicit def toDom(s: String): Document = (DocumentBuilderFactory.newInstance().newDocumentBuilder()).parse(new InputSource(new StringReader(s)))
val mdoc: Monitor[Document] = "value"
mdoc { content } must beEqualTo("value")
Which creates an instance from the serialized XML and then extracts the complete text content of the XML.
The Monitor.apply uses an implicit function to ensure the mutable data is not exposed. It also provides a particularly tidy usage, as seen in the above example. The gruesome details of the parsing are hidden away and the actual usage is stripped to bare essentials.
This approach does require discipline to ensure only immutable data is returned by any function executed by the Monitor.
The Monitor works quite well for unique domain classes, including
XMLBeans created from XML.
Wrappers of org.w3c.dom.Document are essentially untyped, since they cannot be distinguished one from the other. This is unfortunate when a single service implementation might reference a dozen different XML documents.