1

I am trying to create a Launcher program for my application and was going through the App trait and main method to decide which one to use. The only difference that I found between both is:

(1) Threaded code that references the object will block until static initialization is complete. However, because the entire execution of an object extending Application takes place during static initialization, concurrent code will always deadlock if it must synchronize with the enclosing object.

In my case I have a Launcher which basically initialize a Kafka loader object which keeps on running kafka poll. Below is my Launcher trait:

trait Launcher extends LazyLogging {
val config: Config
val actorSystem: ActorSystem
val sourceMessagesType = config.getString("app.source.dataType")
val targetTopic = config.getString("app.kafka.targetTopic")
val targetTopicPartitions =config.getInt("app.kafka.targetTopicPartitions")

val routingManager = HashedPartitionRoutingManager(targetTopic, targetTopicPartitions)
logger.info(s"Initializing MorpheusStreamDataLoader for $sourceMessagesType type")
sourceMessagesType.toLowerCase match {
case "json" => JSonDataLoader(config, routingManager)
case "avro" => AvroDataLoader(config, routingManager)
case _ => throw new IllegalArgumentException
  s"Messages of type ${sourceMessagesType.toLowerCase} are not supported.\n" 
 }
}

Now to launch my application I was trying to find which is best to use, App or main method. However main method implementation doesn't work at all:

object ClientLauncher extends  Launcher {
def main(args: Array[String]): Unit = {
override val config=ConfigFactory.load(args(0))
override val actorSystem: ActorSystem=ActorSystem("ClientActorSystem")
 }
}

When I do this I get error on override modifier that override modifier is not allowed here. However if I use App trait it doesn't gives me any compile time error.

object ClientLauncher extends App with Launcher {
override val config=ConfigFactory.load(args(0))
override val actorSystem: ActorSystem=ActorSystem("ClientActorSystem")
}

The information that I got after reading couple of posts about App trait and main was that there is not difference other than the delayed initialization that happens when we use App trait. Why override doesn't works for main method and works for App? and what is the best way for me to launch my application?

Explorer
  • 1,491
  • 4
  • 26
  • 67

1 Answers1

2

You need to move them outside of the method so that they are object fields instead of local variables.

object ClientLauncher extends  Launcher {

  override val config=ConfigFactory.load()
  override val actorSystem: ActorSystem=ActorSystem("ClientActorSystem")

  def main(args: Array[String]): Unit = {
    /*code*/
  }
}

You won't be able to access command-line arguments or anything else local to main this way though.

If you need access to that and don't want to extend App, an alternative is to use a separate class to extend launcher.

class ClientLauncher(configFile: String) extends  Launcher {
  override val config=ConfigFactory.load(configFile)
  override val actorSystem: ActorSystem=ActorSystem("ClientActorSystem")
}

object Main {
  def main(args: Array[String]): Unit = {
    new ClientLauncher(args(0))
  }
}

Or to pass in those arguments as system properties instead of command line arguments.

object ClientLauncher extends  Launcher {

  override val config=ConfigFactory.load(sys.props("configFile"))
  override val actorSystem: ActorSystem=ActorSystem("ClientActorSystem")

  def main(args: Array[String]): Unit = {
    /*code*/
  }
}

and pass in the system property when you run your code

java -DconfigFile="config.conf" -jar myJar.jar
puhlen
  • 8,400
  • 1
  • 16
  • 31
  • `args(0)` can't be referenced at that location. – jwvh Oct 10 '17 at 19:56
  • but if I do that the scope of `args(0)` is changed and it is no more available to config. – Explorer Oct 10 '17 at 20:06
  • @Explorer right, you can make it a var and assign in main, use a different class to extend launcher and override those values, or keep using the App trait. – puhlen Oct 10 '17 at 20:12
  • @Explorer assuming the config factory is from the typesafe config library, you can just use `ConfigFactory.load()` to have it automatically find you application.conf file without needing to pass it in as an argument. Another option would be to pass in `args(0)` as a system property instead. – puhlen Oct 10 '17 at 20:14
  • I like the idea of passing config as a system property, I was unaware about it, thanks for pointing it out. – Explorer Oct 10 '17 at 21:01