Um die Konfiguration der Weblogic-Domänen und -Server bei meinem derzeitigen Kunden in den Griff zu bekommen, habe ich ein Tool entwickelt, das Domänenkonfigurationsdateien liest und in einen Graphen übersetzt. Ich beschloss, dieses Problem in Scala zu lösen, vor allem weil ich von den leistungsstarken nativen XML-Parsing-Funktionen gelesen hatte. Das Parsen von XML erwies sich als ein Kinderspiel, aber ich konnte etwas darüber lernen, wie man Probleme mit Scala lösen kann.
Um mein Problem zu lösen, musste ich die Konfigurationsdatei für eine Weblogic-Domäne analysieren, oder genauer gesagt (in meinem Fall) die Datei, in der Weblogic Informationen über JMS-Ressourcen speichert. Das Parsen einer dieser Dateien läuft folgendermaßen ab:
[scala]
val configData: Elem = XML.loadFile(jmsConfigFileName)
[/scala]
configData enthält nun eine geparste Version der Datei, deren Name in jmsConfigFileName gespeichert ist. Diese Datei enthält Dinge wie Warteschlangen- und Topic-Definitionen, wie das folgende Fragment:
[xml]
<Warteschlange 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>
[/xml]
Mein erster Versuch, alle Themendefinitionen aus dieser Konfigurationsdatei abzurufen, war folgender:
[scala]
def findTopics(configData: Elem): Set[JmsObject] = {
val jmsObjects = for (topic <[/scala] - (configData "topic"))
yield (new JmsTopic((topic "@name").text, (topic "jndi-name").text))
jmsObjects.toSet
}
Ich habe mich sehr gefreut, diese leistungsstarke und prägnante Schleife geschrieben zu haben. Das for-Konstrukt von Scala ist sehr leistungsfähig, ebenso wie sein XML-Parsing. Um die Warteschlangen abzurufen, habe ich die folgende Methode hinzugefügt:
[scala]
def findQueues(configData: Elem): Set[JmsObject] = {
val jmsObjects = for (queue <[/scala] - (configData "queue"))
yield (new JmsQueue((queue "@name").text, (queue "jndi-name").text))
jmsObjects.toSet
}
Die Duplizierung war offensichtlich, so dass ich mit meiner Lösung nicht ganz so glücklich war, aber ich konnte keinen einfachen Ausweg sehen. Da kam mein Kollege und Scala-Trainer Urs Peter ins Spiel. Er zeigte mir zwei Möglichkeiten zur Verbesserung des Codes.
Unser erster Versuch sieht so aus:
[scala]
def buildListOfJmsObjectsFromConfigData[T]
(configData:Elem, clazz:Class[T], startNode:String): Set[T] = {
val jmsObjects = for (node <[/scala] - (configData startNode))
yield ( clazz.getConstructors.apply(0).newInstance(
(node "@name").text
, (node "jndi-name").text).asInstanceOf[T])
jmsObjects.toSet
}
Die Sie dann wie folgt aufrufen können:
[scala]
buildListOfJmsObjectsFromConfigData (configData, classOf[JmsConnectionFactory]
, "connection-factory")
[/scala]
Das sah für mich ziemlich javamäßig aus, außerdem war es komplex und verschleiert.
Unser zweiter Versuch entfernt die Schleife und verwendet Currying. Er sieht folgendermaßen aus:
[scala]
def buildListOfJmsObjectsFromConfigData[T]
(configData:Elem, startNode:String) (f:(NodeSeq) => T): Set[T] = {
(configData startNode).map(f).toSet
}
[/scala]
Diese zweite Variante von buildListOfJmsObjectsFromConfigData[T] kann wie folgt aufgerufen werden:
[scala]
buildListOfJmsObjectsFromConfigData (configData, "topic") {
node => new JmsTopic((node "@name").text, (node "jndi-name").text)
}
[/scala]
Dies funktioniert, weil (configData startNode) einen Satz von Objekten liefert, auf denen wir map aufrufen. Map nimmt eine Funktion als Parameter an. Die oben gezeigte Funktion extrahiert ein JmsTopic-Objekt, so dass wir am Ende eine Sammlung von JmsTopic-Instanzen haben.
Verfasst von

Jan Vermeir
Developing software and infrastructure in teams, doing whatever it takes to get stable, safe and efficient systems in production.
Unsere Ideen
Weitere Blogs
Contact



