I am a moderately new Scala developer working mostly by myself, so I don't have anyone to tell me what I'm doing wrong (except that the system mostly does work, so it can't be too awful). I would appreciate some feedback on how to make better use of Scala as well as any comments about obvious performance issues.
The class below is part of our utility library. It's part of our OSGi-like infrastructure. I didn't use OSGi because the web application is hosted on a Tomcat server but I liked many of the ideas behind it, so I've incorporated them into my own library.
Thank you!
package edu.stsci.efl.ml
import scala.collection.mutable.{HashSet, Stack, HashMap}
import scala.xml.Node
import java.util.Date
/**
* Provides a container for services that are being provided by various modules.
*
* The default context can be had from the ModuleLoader object.
*
* Services are accessed by the class of the trait that they implement. For example,
* logging services implement LoggerFactoryService so to get access the service, call
* getService(classOf[LoggerFactoryService]) to find the service that was defined in
* the ModuleLoader configuration file.
*/
class EFLContext(val name: String) {
private var modules: HashMap[String, EFLModule] = new HashMap[String, EFLModule]
private val shutdownList: Stack[EFLModule] = new Stack[EFLModule]
private val importList: Stack[EFLModule] = new Stack[EFLModule]
private val services = new HashSet[Object]
private var myDependencies: List[EFLContext] = null
private var myDependents: List[EFLContext] = null
/**
* Used in testing to verify that the correct number of modules have been loaded.
*/
def getModuleCount = modules.size
/**
* Use by the ModuleLoader to pass a module definition from the XML configuration file.
*/
def load(moduleDefinition: Node) {
val moduleName = (moduleDefinition \ "@name").toString()
val className = (moduleDefinition \ "@class").toString()
try {
val rawClass = Class.forName(className)
val moduleClass = rawClass.asSubclass(classOf[EFLModule])
val instance = moduleClass.newInstance
instance.start(this, moduleDefinition)
shutdownList.push(instance)
modules.put(moduleName, instance)
}
catch {
case e: Exception => {
println("Failed to initialize module. Error:")
e.printStackTrace()
throw new EFLInitializationFailure(moduleDefinition.toString(), e)
}
}
}
/**
*
*/
def resolveImport(data: Node) {
val contextName = getAttribute(data, "context")
val name = getAttribute(data, "name")
val module = {
val context = ModuleLoader.getContext(contextName)
if (context == null) null
else {
val result = context.modules.getOrElse(name, null)
if (result != null) addDependency(context)
result
}
}
if (module == null) throw new IllegalArgumentException("Unable to resolve import of module '" + name + "' in context '" +
contextName + "'.")
module.addContext(this)
importList.push(module)
modules.put(name, module)
}
def getAttribute(data: Node, name: String): String = {
val attribute = data.attribute(name)
if (attribute.isEmpty) throw new IllegalArgumentException("Failed to specify attribute " + name + " in import.")
attribute.head.toString()
}
/**
* Used by modules at application startup to announce services that they are providing.
*/
def addService(service: Object) {
services += service
}
/**
*
*/
def addDependency(fLContext: EFLContext) {
myDependencies match {
case null => myDependencies = List[EFLContext](fLContext)
case _ => myDependencies = myDependencies :+ fLContext
}
fLContext.addDependent(this)
}
/**
*
*/
def addDependent(fLContext: EFLContext) {
myDependents match {
case null => myDependents = List[EFLContext](fLContext)
case _ => myDependents = myDependents :+ fLContext
}
}
def hasDependents: Boolean = (myDependents != null)
def removeDependency(context: EFLContext) {
myDependencies = myDependencies.filterNot((c: EFLContext) => c == context)
if (myDependencies.isEmpty) myDependencies = null
context.removeDependent(this)
}
def removeDependent(context: EFLContext) {
myDependents = myDependents.filterNot((c: EFLContext) => c == context)
if (myDependents.isEmpty) myDependents = null
}
/**
* Used by modules at application shutdown to terminate services.
*/
def removeService(service: Object) = {
services -= service
}
/**
* Called by the ModuleLoader at application shutdown so that loaded modules can be stopped.
*/
def shutdown() {
if (modules == null) throw new RuntimeException("Attempt to shutdown context that was already shutdown.")
if (hasDependents) throw new RuntimeException("Attempt to shutdown context with loaded dependent contexts.")
while (!importList.isEmpty) {
val next = importList.pop()
next.removeContext(this)
}
while (!shutdownList.isEmpty) {
val next = shutdownList.pop()
next.stop(this)
}
modules.clear()
if (services.size > 0) {
println("Improper cleanup: " + services.size + " services still registered.")
services.foreach((s) => println(s))
}
modules = null
while (myDependencies != null) {
val next = myDependencies.head
removeDependency(next)
}
}
/** Search for a given service by the class/trait the service implements/extends.
*
* @param serviceClass The class of a service that is needed.
* @return Option[A] 'Some' of the given type, or 'None' if no service object extending that type is registered.
*/
def findService[A](serviceClass: Class[A]): Option[A] = {
services.find(serviceClass.isInstance).asInstanceOf[Option[A]]
}
/** Search for a given service by the class/trait the service implements/extends.
*
* @return Service of the given type, or null if no service object extending that type is registered.
*/
@Deprecated
def getService[A](serviceClass: Class[A]): A = {
val temp = services.find(serviceClass.isInstance)
if (temp == None) {
null.asInstanceOf[A]
}
else {
temp.get.asInstanceOf[A]
}
}
}