public class ContentCreationJobFactory implements JobFactory {
@Autowired
private ContentCreationService contentCreationService;
public ContentCreationJobFactory() {
}
@Override
public boolean accepts(@NonNull String jobType) {
return jobType.equals("createContent");
}
@NonNull
@Override
public Job createJob() {
return new CreateContentJob(contentCreationService);
}
}
How to use Studio's Jobs Framework - CMCC 12
This tutorial shows how to use the jobs framework to trigger a background process via a user action and monitor its progress. In the given example, the result of the job will be newly created content.
Implementing the Studio Server Part
JobFactory
Let’s start with the Java part of the implementation. To define a new job, you first have to set up a new com.coremedia.rest.cap.jobs.JobFactory
that is responsible for creating new com.coremedia.rest.cap.jobs.Job
instances:
The factory implementation is quite simple. It implements the interface JobFactory
and tells the framework to create new instances of class CreateContentJob
. It also checks if it is responsible for a given job type. In this case it only accepts jobs with the type "createContent". Additionally, we have injected the class ContentCreationService here
, which takes care of the actual content creation.
Next, you have to add the factory to your Spring configuration:
@Bean
public ContentCreationJobFactory contentCreationJobFactory() {
return new ContentCreationJobFactory();
}
Next, you will take a closer look at the Job implementation.
Job
public class CreateContentJob implements Job {
private String targetFolder;
private ContentCreationService contentCreationService;
CreateContentJob(ContentCreationService contentCreationService) {
this.contentCreationService= contentCreationService;
}
@Nullable
@Override
public List<Content> call(@NonNull JobContext jobContext) throws JobExecutionException {
return contentCreationService.createContent(targetFolder, jobContext);
}
@SuppressWarnings("unused") //used by JobsFramework
public void setTargetFolder(String targetFolder) {
this.targetFolder = targetFolder;
}
}
In this example, the whole logic of the actual content creation is delegated to the ContentCreationService
which we will cover in the next section. The interesting part here is the property targetFolder
. The value is passed by the client as JSON parameter during the job creation in the Studio client API. To make this parameter known to the Java implementation, you have to define a corresponding setter method here, in this case public void setTargetFolder(String targetFolder)
.
The parameter is passed later to the ContentCreationService
.
Jobs and JobContext
Let’s see how the actual implementation in the ContentCreationService
may look like:
List<Content> createContent(String targetFolder, JobContext jobContext) {
List<Content> createdContent = new ArrayList<>();
//progress calculation for the executing job
int amountToProcess = 10;
for (int i=0; i<amountToProcess ; i++) {
//TODO implement actual content creation here and add it to the result 'createdContent'
//progress notification for the executing job
jobContext.notifyProgress(i / amountToProcess);
}
return createdContend;
}
For the sake of simplicity, we haven’t implemented the details of the content creation here. The important part is the usage of the JobContext
here. Each time a content is created, the framework and also the client get notified, that progress has been made. The Job is finished once the amount of progress equals 1.
The result of the job is returned to the client. In this example, it is a list of Content instances which will be converted to client-side Content RemoteBeans automatically. If you pass any other data, ensure that it can be serialized to JSON.
The Java part for creating a new Job is finished. Let’s take a look on the client-side next.
Implementing the Studio Client Part
Similar to the Java backend, you also have to declare a job implementation for the Studio client:
public class CreateContentJob extends RemoteJobBase implements BackgroundJob {
private var targetFolder:String
private var ctx:JobContext;
public function CreateContentJob(targetFolder:String) {
this.targetFolder = targetFolder;
}
override public function execute(jobContext:JobContext):void {
this.ctx = jobContext;
super.execute(jobContext);
}
override protected function getJobType():String {
return "createContent";
}
override protected function getParams():Object {
return {
targetFolder: targetFolder
};
}
override protected function mayRetry():Boolean {
return false;
}
public function getNameExpression():ValueExpression {
return ValueExpressionFactory.createFromValue(msg);
}
public function getIconClsExpression():ValueExpression {
return ValueExpressionFactory.createFromValue(item.getTypeCls());
}
public function getErrorHandler():Function {
}
public function getSuccessHandler():Function {
}
}
The class extends from RemoteJobBase
which provides some basic functionality for jobs. The implementation of the interface BackgroundJob
ensures that the visualization of the progress is added to the Jobs window, which is located in the upper right corner of Studio. The interface requires the implementation of the following methods:
-
getNameExpression(): Returns a
ValueExpression
which contains the name of the job to display. -
getIconClsExpression(): Returns a
ValueExpression
which contains the icon class to display for this job. -
getErrorHandler(): The method to call when the error button is displayed in case of an error.
-
getSuccessHandler(): The method to call when the success button is displayed after the job has been finished.
Also, note that the getParams()
method returns the value for the targetFolder
parameter as defined in the Java backend. The job type matches the one in Java defined too: "createContent".
Finally, let’s take a look how the job is invoked:
var job:CreateContentJob = new CreateContentJob(targetFolder);
var trackedJob:TrackedJob = jobService.executeJob(job,
//on success
successCallback,
//on error
function (result:Object):void {
});
This code can be added as handler implementation for an action or a button. Again it supports an error and a success handler which are called as a result of the actual REST call.
Summary
This How-To gave a brief introduction into Studio’s job framework. Feel free to leave a comment if you miss something.