Skip to content

Application Core

Angel Sanadinov edited this page Jul 19, 2017 · 4 revisions

Components

A core3 application is made up of a number of components based on Akka Actors and they implement the core3.core.Component and core3.core.ComponentCompanion traits.

All of them can be managed by a core3.core.ComponentManager, normally configured in an app's Module definition. For example:

import akka.actor.ActorSystem
import akka.util.Timeout
import core3.config.StaticConfig
import com.google.inject.{AbstractModule, Provides, Singleton}
import core3.core.{ComponentManager, ComponentManagerActor}
import core3.database.dals.DatabaseAbstractionLayer
import core3.workflows.WorkflowEngine

import scala.concurrent.ExecutionContext
import scala.concurrent.duration._

class Module extends AbstractModule {

  ...

  @Provides
  @Singleton
  def provideComponentManager(
    system: ActorSystem,
    engine: WorkflowEngine,
    db: DatabaseAbstractionLayer
  )(implicit ec: ExecutionContext): ComponentManager = {
    val managerConfig = StaticConfig.get.getConfig("manager")
    implicit val timeout = Timeout(
      managerConfig.getInt("requestTimeout").seconds
    )

    new ComponentManager(
      system.actorOf(
        ComponentManagerActor.props(
          Map(
            "engine" -> engine.getRef,
            "db" -> db.getRef
          )
        )
      )
    )
  }

  ...

}

Defining a component

In principle, only the Component trait needs to be extended, however, providing a ComponentCompanion object allows the CLI to integrate that component and process actions for it. Following is a simple example with two actions/commands:

import akka.actor.Props
import core3.core.{Component, ComponentCompanion}
import core3.core.Component.{ActionDescriptor, ActionResult}
import play.api.libs.json.Json
import scala.concurrent.{ExecutionContext, Future}

class TestComponent(implicit ec: ExecutionContext) extends Component {

  //... some other definitions ...

  private var count_ExecuteAction: Long = 0

  override protected def shutdown(): Unit = {
    //... some cleanup code (if required) ...
  }

  override protected def handle_ExecuteAction(
    action: String,
    params: Option[Map[String, Option[String]]]
  ): Future[ActionResult] = {
    count_ExecuteAction += 1

    Future {
      action.toLowerCase match {
        case "stats" =>
          ActionResult(
            wasSuccessful = true,
            message = None,
            data = Some(
              Json.obj(
                "counters" -> Json.obj(
                  "executeAction" -> count_ExecuteAction,
                  "anotherStat" -> "some value"
                )
              )
            )
          )

        case "anotherAction" =>
          ActionResult(
            wasSuccessful = false,
            message = Some("Not Implemented")
          )
      }
    }
  }

  //... actual component implementation ...

}

object TestComponent extends ComponentCompanion {
  def props()(implicit ec: ExecutionContext): Props = Props(
    classOf[TestComponent], ec
  )

  //... other companion definitions ...

  override def getActionDescriptors: Seq[ActionDescriptor] = {
    Seq(
      ActionDescriptor(
        "stats",
        "Retrieves the latest component stats",
        arguments = None
      ),
      ActionDescriptor(
        "anotherAction",
        "Some other action",
        arguments = Some(Map("argName1" -> "(required) Required argument #1")))
    )
  }
}

Local Console

Allows the management of a core3-based application, when started in the foreground with the appropriate CLI flag (application-defined). For example (starts the core3-example-engine, with the console enabled):

sbt run -Dhttps.port=9900 -Dhttp.port=disabled -Dc3ee.console=enabled

Currently, the local console behaves normally only when the application is started directly with a jar and not via sbt / Play.

Default commands

  • help - shows the help menu
  • quit - terminates the application
  • version - shows version information
  • system - allows management of the application's core (configuration, state, etc)

Component commands

Each component supplies it's own set of commands; see the running app's help menu for more information.

Creating the CLI

Creating the LocalConsole in a Play app requires two things: the console provider (in Module.scala) and a simple class to "require" that console.

Provider example

import akka.actor.ActorSystem
import akka.util.Timeout
import com.google.inject.{AbstractModule, Provides, Singleton}
import core3.config.StaticConfig
import core3.core.Component.ComponentDescriptor
import core3.core.cli.LocalConsole
import core3.database._
import core3.database.dals.json.Redis
import core3.database.dals.DatabaseAbstractionLayer
import core3.workflows._

import scala.concurrent.ExecutionContext
import scala.concurrent.duration._

class Module extends AbstractModule {

  //.. other providers ...

  @Provides
  @Singleton
  def provideLocalConsole(
    manager: ComponentManager,
    engine: WorkflowEngine,
    db: DatabaseAbstractionLayer
  )(implicit ec: ExecutionContext): Option[LocalConsole] = {
    Option(System.getProperty("c3ee.console")) match {
      case Some("enabled") =>
        val appVendor: String = core3_example_engine.BuildInfo.organization
        val appName: String = core3_example_engine.BuildInfo.name
        val appVersion: String = core3_example_engine.BuildInfo.version

        val managerConfig = StaticConfig.get.getConfig("manager")
        implicit val timeout = Timeout(
          managerConfig.getInt("requestTimeout").seconds
        )

        Some(
          LocalConsole(
            appVendor,
            appName,
            appVersion,
            manager.getRef,
            Seq(
              ComponentDescriptor(
                "engine",
                "The system's workflow engine",
                core3.workflows.WorkflowEngineComponent
              ),
              ComponentDescriptor(
                "db",
                "The system's core DB",
                core3.database.dals.Core
              ),
              ComponentDescriptor(
                "bootstrap",
                "The system's initializer",
                BootstrapComponent
              )
            )
          )
        )

      case _ => None
    }
  }
}

ConsoleStart

import javax.inject.{Inject, Singleton}

import core3.core.cli.LocalConsole
import play.api.Environment

import scala.concurrent.{ExecutionContext, Future}

trait ConsoleStart

@Singleton
class ConsoleStartImpl @Inject()(
  console: Option[LocalConsole]
)(implicit environment: Environment, ec: ExecutionContext)
extends ConsoleStart {
  Future {
    console.foreach(_.start())
  }
}

Clone this wiki locally