Server Jobs
Create static and dynamic Server Jobs.
Constraints
You have created the project with scala-adapters-g8
Job Configuration
The jobs are configured in the reference.conf
, here from the scala-adapters
project.
```
job.configs = [{
ident = "demoJob"
schedule {
// the first time of day the Import should run (this is the Server time!). (format is HH:mm)
// Default is 01:00
first.time = "03:00"
// the first weekday if needed (e.g. to emulate once a week)
// possible: "monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday" or "-" for nothing
first.weekday = "tuesday"
// the period the Adapter should call the Coop Webservice
// Default is one day (1440 minutes) - be aware 1 minute is the smallest period possible
// - and it must be greater than the time the import takes!
// make also sure that the period is so that the import is at always the same time of day.
interval.minutes = 2
}
}, {
ident = "demoJobWithDefaultScheduler"
schedule {
}
}, {
ident = "demoJobWithoutScheduler"
}]
```
You see above the possible configurations (with- and without a Scheduler defined). Each Job needs a unique ident!
Job Creation
You have to provide a Factory that creates the JobProcess vor a JobConfig.
Static Creation
Static creation means that the Jobs are created at Startup in the trait JobCreation
.
All you need to do shows the example from scala-adapters
```
// the Factory must be a Singleton
@Singleton
class DemoJobCreation @Inject()(demoJob: DemoJobProcess // each JobProcess is injected
, demoJobWithDefaultScheduler: DemoJobWithDefaultSchedulerActor
, demoJobWithoutScheduler: DemoJobWithoutSchedulerActor
, @Named("actorSchedulers") val actorSchedulers: ActorRef // needed to create Schedules
, actorSystem: ActorSystem // needed to create the JobActors
)(implicit val ec: ExecutionContext) // needed for async processing
extends JobCreation {
// create all JobActors
private lazy val demoJobRef = actorSystem.actorOf(JobActor.props(jobConfigs(demoJobIdent), demoJob), demoJobIdent)
private lazy val demoJobWithDefaultSchedulerRef = actorSystem.actorOf(JobActor.props(jobConfigs(demoJobWithDefaultSchedulerIdent), demoJobWithDefaultScheduler), demoJobWithDefaultSchedulerIdent)
private lazy val demoJobWithoutSchedulerRef = actorSystem.actorOf(JobActor.props(jobConfigs(demoJobWithoutSchedulerIdent), demoJobWithoutScheduler), demoJobWithoutSchedulerIdent)
// return the correct JobActor for a JobConfig
def createJobActor(jobConfig: JobConfig): ActorRef = jobConfig.jobIdent match {
case "demoJob" => demoJobRef
case "demoJobWithDefaultScheduler" => demoJobWithDefaultSchedulerRef
case "demoJobWithoutScheduler" => demoJobWithoutSchedulerRef
case other => throw ServiceException(s"There is no Job for $other")
}
}
```
Dynamic Creation
Dynamic creation means that the Jobs are created at Runtime in your Factory. The example is from a Calendar integration, where different clients use different Service-URLs. (only differences to static creation are explained)
```
@Singleton
class CalendarJobCreation @Inject()(calendarImporter: CalendarImporter // the import infrastructure
, calendarService: CalendarService // the service to integrate
, @Named("actorSchedulers") val actorSchedulers: ActorRef
, actorSystem: ActorSystem
)(implicit val mat: Materializer
, val ec: ExecutionContext)
extends JobCreation {
def createJobActor(jobConfig: JobConfig): ActorRef = create(jobConfig)
private def create(jobConfig: JobConfig): ActorRef = {
// creates a JobProcess from the JobConfig, that includes a subWebPath sent by the client.
val process = CalendarProcess(jobConfig, calendarImporter, calendarService)
val jobActor = actorSystem.actorOf(JobActor.props(jobConfig, process))
initSchedule(jobConfig, jobActor)
jobActor
}
// return empty Map onStartUp - as the JobProcesses are created on the fly
override def createJobActorsOnStartUp(): Map[JobConfig, ActorRef] = Map()
}
```
Job Process
Implements the Job logic itself, like:
- Server Batch Jobs
- Handling Client Requests
- Implement Chat-Bot
- etc.
Here the implementation you have in your get-started Project.
```
class GetStartedProcess @Inject()()
(implicit val mat: Materializer, val ec: ExecutionContext)
extends JobProcess {
val jobLabel = "GetStarted Job"
def createInfo(): ProjectInfo = // check createInfo for adding more infos!
createInfo(version.BuildInfo.version)
// the process fakes some long taking tasks that logs its progress
def runJob(user: String)
(implicit logService: LogService
, jobActor: ActorRef): Future[LogService] = {
Future {
logService.startLogging()
val results = ... // e.g. call a service
results.foreach(doSomeWork)
logService // fluent api
}
}
protected def doSomeWork(dr: GetStartedResult)
(implicit logService: LogService): LogEntry = {
...
logService.log(ll, s"Job: $jobLabel $ll: ${dr.name}", detail)
}
}
```
Register Factory
Now you have to tell Guice (Dependency Injection) what class is responsible for the Job creation.
In your get-started Project this already done in Module
.
```
class Module extends AbstractModule with AkkaGuiceSupport {
def configure(): Unit = {
bind(classOf[JobCreation])
.to(classOf[GetStartedJobCreation])
.asEagerSingleton() // make it eager - as the Job is static and created at startup
... // more configuration
}
}
```
Check the Result
Run the Project, as described here get-started::run
If you have only one Job, then http://localhost:9000 is all you need.
If you have more than one Job, you use http://localhost:9000/jobProcess/JOB_NAME
.
By default it takes the first Job of the configuration.