[MIL5-175] - Avoiding concurrent class load - using synchronized hash map
diff --git a/fey-core/src/main/scala/org/apache/iota/fey/Ensemble.scala b/fey-core/src/main/scala/org/apache/iota/fey/Ensemble.scala
index 3f74cec..04308c3 100644
--- a/fey-core/src/main/scala/org/apache/iota/fey/Ensemble.scala
+++ b/fey-core/src/main/scala/org/apache/iota/fey/Ensemble.scala
@@ -218,24 +218,32 @@
* @return Props of actor based on JSON config
*/
private def getPerformer(performerInfo: Performer, connections: Map[String, ActorRef]): Props = {
+ var clazz:Option[Class[FeyGenericActor]] = None
- val clazz = loadClazzFromJar(performerInfo.classPath, s"${performerInfo.jarLocation}/${performerInfo.jarName}", performerInfo.jarName)
-
- val dispatcher = if(performerInfo.dispatcher != "") s"fey-custom-dispatchers.${performerInfo.dispatcher}" else ""
-
- val actorProps = Props(clazz,
- performerInfo.parameters, performerInfo.backoff, connections, performerInfo.schedule, orchestrationName, orchestrationID, performerInfo.autoScale)
-
- // dispatcher has higher priority than controlAware. That means that if both are defined
- // then the custom dispatcher will be used
- if(dispatcher != ""){
- log.info(s"Using dispatcher: $dispatcher")
- actorProps.withDispatcher(dispatcher)
+ Utils.loadedJars.synchronized {
+ clazz = Some(loadClazzFromJar(performerInfo.classPath, s"${performerInfo.jarLocation}/${performerInfo.jarName}", performerInfo.jarName))
}
- else if(performerInfo.controlAware){
- actorProps.withDispatcher(CONFIG.CONTROL_AWARE_MAILBOX)
+
+ if(clazz.isDefined) {
+ val dispatcher = if (performerInfo.dispatcher != "") s"fey-custom-dispatchers.${performerInfo.dispatcher}" else ""
+
+ val actorProps = Props(clazz.get,
+ performerInfo.parameters, performerInfo.backoff, connections, performerInfo.schedule, orchestrationName, orchestrationID, performerInfo.autoScale)
+
+ // dispatcher has higher priority than controlAware. That means that if both are defined
+ // then the custom dispatcher will be used
+ if (dispatcher != "") {
+ log.info(s"Using dispatcher: $dispatcher")
+ actorProps.withDispatcher(dispatcher)
+ }
+ else if (performerInfo.controlAware) {
+ actorProps.withDispatcher(CONFIG.CONTROL_AWARE_MAILBOX)
+ } else {
+ actorProps
+ }
}else{
- actorProps
+ log.error(s"Could not load class for performer ${performerInfo.uid}")
+ throw new ClassNotFoundException(s"${performerInfo.jarName} -- ${performerInfo.jarLocation}")
}
}
diff --git a/fey-core/src/main/scala/org/apache/iota/fey/Utils.scala b/fey-core/src/main/scala/org/apache/iota/fey/Utils.scala
index 5577ea6..3bfde54 100644
--- a/fey-core/src/main/scala/org/apache/iota/fey/Utils.scala
+++ b/fey-core/src/main/scala/org/apache/iota/fey/Utils.scala
@@ -42,6 +42,7 @@
* Keeps the loaded clazz in memory
* JARNAME,[CLASSPATH, CLASS]
*/
+
val loadedJars: HashMap[String, (URLClassLoader, Map[String, Class[FeyGenericActor]])]
= HashMap.empty[String, (URLClassLoader, Map[String, Class[FeyGenericActor]])]
@@ -72,9 +73,11 @@
loadedJars.get(jarName) match {
case None =>
+ log.info(s"Loading Jar: $jarName")
val urls:Array[URL] = Array(new URL(s"jar:file:$path!/"))
- val cl: URLClassLoader = URLClassLoader.newInstance(urls)
+ val cl: URLClassLoader = URLClassLoader.newInstance(urls, getClass.getClassLoader)
val clazz = cl.loadClass(className)
+ log.info(s"Loading Class $className with path $path")
val feyClazz = clazz.asInstanceOf[Class[FeyGenericActor]]
log.info(s"$path -> $className")
loadedJars.put(jarName, (cl, Map(className -> feyClazz)))
@@ -83,6 +86,7 @@
case Some(loadedJar) =>
loadedJar._2.get(className) match {
case None =>
+ log.info(s"Loading Class $className with path $path")
val clazz = loadedJar._1.loadClass(className)
val feyClazz = clazz.asInstanceOf[Class[FeyGenericActor]]
loadedJars.put(jarName, (loadedJar._1, Map(className -> feyClazz) ++ loadedJar._2))