Background Task - a.k.a the secret weapon behind the new asynchronous staging

The more refined staging along with Liferay Portal 6.2 has been out for a while and you might have read Mate Thurzo’s great blog entries about the topic, yet I think it is also worth the time to talk about a utility framework we developed together with but not only for staging!

The Background Task framework allows the developers to create and manage any kind of tasks that need to be run in a separated thread without the need of knowing anything about JAVA threading or concurrency. Behind the scenes it uses the robust messaging framework of Liferay that does the trick for us, because its message listener invocation mechanism provides the multi-threaded execution for all the tasks. With the help of this new framework we are able to make the export/import processes asynchronous, while we also have the option to continuously monitor the running tasks and check their results.

 

Let’s see how it works

Behind the framework there is a new entity called BackgroundTask which stores all the information the task execution requires, like:

  • servletContextNames: the servlet context names to get the ClassLoaders where the task executor class can be found. If it is null then the default portal class loader is used to determine the task executor class.
  • taskExecutorClassName: the name of a class implements the BackgroundTaskExecutor interface
  • taskContextMap: all the context variables the task execution requires, stored in JSON format
  • completed: a simple flag shows whether the task execution is completed or not
  • status: the current status of the task execution, the available status constants are in the BackgroundTaskConstants class.
  • attachments: you can add FileEntry type objects as attachments for later usage. This feature uses the Portlet File Repository framework to store the entries in a separate folder for each and every BackgroundTask entity.

 

As you can see the entity doesn’t know anything about the business logic we would like to implement, but it has the reference to a class that implements it. The BackgroundTaskExecutor interface has the following methods:

  • BackgroundTaskResult execute(BackgroundTask backgroundTask)

This one is pretty straightforward. You can implement the business logic in this method, but the interesting part is the return value. A BackgroundTaskResult object is a int status - String statusMessage pair that stores information about the result of the task execution.

  • BackgroundTaskStatusMessageTranslator getBackgroundTaskStatusMessageTranslator()

Another powerful feature of the API is the option to send and consume status messages during the execution of the task. The only thing we have to do is to send a message object on the Liferay Message Bus to a specific destination called "liferay/background_task_status" and put the current background task id to the message with key “backgroundTaskId”.

  • String handleException(BackgroundTask backgroundTask, Exception e)

This method is called when a generic Exception is thrown during the task execution. The return value of the method is the status message of the BackgroundTaskResult of the current execution.

  • boolean isSerial()

If this method returns true it means only one task of a given type can run parallel and all the newly created tasks get queued.

 

Examples

After the theory let’s see some examples how to use the framework in some basic scenarios.

 

Implement the task executor:

public class MyBackgroundTaskExecutor extends BaseBackgroundTaskExecutor {  
    public MyBackgroundTaskExecutor() {
        setBackgroundTaskStatusMessageTranslator(
            new MyBackgroundTaskStatusMessageTranslator ());
        setSerial(true);
    }

    @Override
    public BackgroundTaskResult execute(BackgroundTask backgroundTask)
        throws Exception {

        Map taskContextMap = backgroundTask.getTaskContextMap();

        // do your business logic here
    }
}

 

Create a new BackgroundTask object

BackgroundTaskLocalServiceUtil.addBackgroundTask(
    userId, sourceGroupId, StringPool.BLANK, null,
    MyBackgroundTaskExecutor.class, taskContextMap,
    serviceContext);

 

Send status messages:

Message message = new Message();

// Background task id needs to be passed

message.put("backgroundTaskId", BackgroundTaskThreadLocal.getBackgroundTaskId());

// Pass all the necessary attributes here

// Send it over the built-in Message Bus to the background task status
// destination

MessageBusUtil.sendMessage(DestinationNames.BACKGROUND_TASK_STATUS, message);

 

Process the message with a custom message translator:

public class MyBackgroundTaskStatusMessageTranslator 
    implements BackgroundTaskStatusMessageTranslator {

    @Override public void translate(
        BackgroundTaskStatus backgroundTaskStatus, Message message) {

        backgroundTaskStatus.setAttribute(
            "xy", doTranslate(message.getString("xy"));

     }
}

 

Get the BackgroundTaskStatus of an existing task

BackgroundTaskStatus backgroundTaskStatus =
    BackgroundTaskStatusRegistryUtil.getBackgroundTaskStatus(
        backgroundTaskId);

System.out.println(backgroundTaskStatus.getAttribute("xy"));

 

Finally I would like to encourage everyone to use this powerful, but still simple framework to run asynchronous tasks, or to put long running jobs like an import process to the background. It has a lot of potential and as you can see it is really easy use to resolve complex problems while you have the freedom to do anything you want.

39
Blogs
This is Awesome.

Just curious to know how this is different from the Quartz Scheduler that Liferay uses at the moment. Again the current implementation of scheduler uses the MessageBus to fire something asynchronously. Appreciate if you can list out the differences of these two mechanisms.

Ahamed Hasan
Author, Liferay Cookbook
I would say there are similarities between these two, like as you mentioned both uses the message bus to make the execution async, but this new one tries to hide this whole mechanism from you, meanwhile the Quartz Scheduler requires to create and register message listener, destinations, etc... for your job.

Another difference is the Quartz Scheduler is strong in scheduled execution, but doesn't really support the job execution monitoring or task error handling. However Background Task doesn't support scheduling (it starts all job immediately), but it has much better monitoring capabilities and it has a built-in attachment storage feature for example.
Very nice and clear article! Thanks

Just a question: what happens to running tasks if liferay (server) crash? Is there a mechanism to resume tasks?
Hi Marco,

unfortunately the framework can't re-start or continue automatically the tasks, but this is a known requirement so we are planning to this in the future, however until that you have to take care of this manually.
Is there a way to recognize these "killed" tasks?
The statuses of these remain STATUS_IN_PROGRESS while there is no BackgroundTaskStatus registered for them in the BackgroundTaskStatusRegistryUtil.

You can "kill" them properly with a
BackgroundTaskLocalServiceUtil.amendBackgroundTask(
backgroundTaskId, null, status, statusMessage, serviceContext); call.
In addition you should check the "Lock_" table and delete the row related to the "killed" task, if it exists.

Then clear all caches and you will be able to run another LAR/Staging process.

All the process can be done using the "Script Panel" (Control Panel --> Server Administration --> Script) and clearing the caches after executing the scripts to avoid having to restart the server.
Nice article about a somewhat hidden feature of the recent framework. The concept of background tasks has several use cases in custom environments. It would be useful to explore the fringes of the technology based on a simple but complete example. This might possibly include alternatives of how to let our implementation known to the framework (plug-in, script, etc.), how to start the task (UI), or check the progress and completion?
Hi Dani,

It's nice to have this blog, though, I'm wondering why it's not in the JavaDoc? Do you plan to add the corresponding method and class description as a JD to the source?

Anyway, thanks for the post!

-
Tibi
Hi Dániel,
i was just trying to implement a simple background task using your presented way. I implemented a simple Portlet and added the implementation, but if start the backgroud task using BackgroundTaskLocalServiceUtil, i get a classNotFound error for the MyBackgroundTaskExecutor class. It only works if i deploy this class using ext-plugin. Is this the desired way to provide BackgroundTasks or am i missing something?
Thanks and regards,
Michel
Hi Michel,

have you tried to pass the context name to the BTLSU.addBackgroundTask() method? We have a mechanism to support class loaders other then the portal one to be able to find the executors deployed from portlets.

To do that the API requires a valid servlet context name, what is the name of the plugin's servlet context.

thanks,
Daniel
Hi Dániel,
thank you very much, this was the point i was missing. As soon as i added the servletContext Name of my portlet, the BackgroundTask got executed properly :-)
Thanks and regards,
Michel
A question, in a cluster enviroment is possible to run background task only on a specific node?
Thank you Daniel for sharing this.
It was useful from me to manage an import process of Lotus Notes documents with attachments into Liferay Documents and Media.

You can find the work (still in progress) on my personal site and on GitHub: www.filippodelprete.com/2014/12/liferay-importing-documents-from-a-domino-server-using-backgroundtask
HI Daniel,

I am able to implement background job by taking reference from your portlet.
But i am not able to show progress bar.

BackgroundTaskStatus returns nothing.

I am executing below code in a loop

migrationStatusVO.setNodesImported(nodesImported); migrationStatusVO.setNodesWithProblem(nodesWithProblem); MigrationDataHandlerStatusMessageSenderUtil.sendStatusMessage(migrationStatusVO);

Please let me know if you have any pointers.
Hi Vipin,

the progress bar is a (currently at least) custom .jsp we have that simply displays the percentage we calculate in the background and stores in the BackgroundTaskStatus object. It means out of the box we do not have a generic solution to display a progress bar based on a background task, but we are working on one that hopefully makes this whole thing easier in the future. (Probably it worth a new blog post once we'll be able to finish this)

So currently I suggest you to follow this design to create your own progress bar:

1, Send status messages from your running process just like you did it
2, Process this message with your custom BackgroundTaskStatusMessageTranslator. This means you need to implement this interface with a class and set this class as the status message translator for your executor. e.g.:

public MyBackgroundTaskExecutor() {
setBackgroundTaskStatusMessageTranslator(new MyBackgroundTaskStatusMessageTranslator());
}
3, Calculate and store your progress information in the BackgroundTaskStatus object when processing the arriving messages

@Override
protected synchronized void translateLayoutMessage(
BackgroundTaskStatus backgroundTaskStatus, Message message) {

// get the migration status object from the message and calculate the actual progress based on that

backgroundTaskStatus.setAttribute("progress", progress);
}

4, From a custom .jsp find the running background task based on its status, executor, etc.. and query up the associated BackgrounTaskStatus object:

BackgroundTaskStatus backgroundTaskStatus = BackgroundTaskStatusRegistryUtil.getBackgroundTaskStatus(backgroundTask.getBackgroundTaskId());

long progress = GetterUtil.getLong(backgroundTaskStatus .getAttribute("progress"));

And based on that you can visualize the progress information based on your needs.

I hope it helps, but let me know if you have any additional questions!
Daniel
Hi Daniel,

Thanks for your reply.
I did the same thing but it sends nothing to BackgroundTaskStatus object.
I print that object in my jsp and it does not have attribute which i set in translator!
Can you somehow make sure you are updating the same object from the message translator that you try to fetch from the UI?
I print the taskId which we use to fetch BackgroundTaskStatus and its same all across the process emoticon

Other than this can you guide me for how to use file repository or attachment with Backgroundtask?
Can you verify that after you call the put it really contains the changed value?

Regarding the attachments you can add an attachment file to a BT with:
BackgroundTaskLocalServiceUtil.addBackgroundTaskAttachment(...)

And you can get the attachments with:
BacgroundTask.getAttachmentsFileEntries()

One useful utility is:
PortletFileRepositoryUtil.getDownloadPortletFileEntryURL(themeDisplay, fileEntry, StringPool.BLANK)

this one generates a download URL for your attachment file.
Hi Daniel,

In TaskStatusMessageTranslator I cannot see values which i sent.
Let me write what I performed till now.

1) I created background job by implementing BaseBackgroundTaskExecutor and set its constructor like

public void MyBackgroundTaskExecutor() {
setBackgroundTaskStatusMessageTranslator(
new MyBackgroundTaskStatusMessageTranslator());
setSerial(true);
}
2) Then in execute method I am performing some task in a loop where i call

statusVO.setNodesImported(nodesImported);
statusVO.setNodesWithProblem(nodesWithProblem);
MyDataHandlerStatusMessageSenderUtil.sendStatusMessage(statusVO);

Note: here I can see the values which i send

3) In MyBackgroundTaskStatusMessageTranslator when i try to print these values

System.out.println("nodesImported : nodesImported : " + message.getLong("nodesImported"));
backgroundTaskStatus.setAttribute(
"nodesImported",
message.getLong("nodesImported"));

It is not even printing : "nodesImported : nodesImported : " text

Can you tell me what is wrong?
Hi Vipin,

I am not sure how MyDataHandlerStatusMessageSenderUtil works and how this statusVO object looks like. I think you send a Message object at the end, so please make sure you put every of the statusVO attributes to the message on-by-one, if you want to access these attributes like message.getLong("nodesImported")) or if you put the whole statusVO to the message directly, then please get it first and then query its attributes like: Object statusVo = message.get("statusVo");

If you reuse the same statusVO object from the code piece where you send the status message from, please clone this object because beforehand because most probably it will change while you are processing the status message.
Hi Dániel,

Thanks for the post, I have tried but getting an error,

Here my code:

ThemeDisplay themeDisplay = (ThemeDisplay) request.getAttribute(WebKeys.THEME_DISPLAY);

Map<String, Serializable> taskContextMap = null;
String taskName = null;
String servletContextNames [] = new String[1] ;

servletContextNames[0] = getClass().getClassLoader().toString();

try {
BackgroundTaskLocalServiceUtil.addBackgroundTask(themeDisplay.getUserId(), themeDisplay.getScopeGroupId(), taskName, servletContextNames,
TestBackgroundTaskExecutor.class, taskContextMap,new ServiceContext());
} catch (PortalException e) {
e.printStackTrace();
} catch (SystemException e) {
e.printStackTrace();
}

====================================================================

public class TestBackgroundTaskExecutor extends BaseBackgroundTaskExecutor {

@Override
public BackgroundTaskResult execute(BackgroundTask backGroundTask) throws Exception {

System.out.println("In BackgroundTaskResult class"+backGroundTask);

return BackgroundTaskResult.SUCCESS;

}
}

==========================================================================

StackTrace :
13:02:37,925 ERROR [liferay/background_task-1][BackgroundTaskMessageListener:133] Unable to execute background task
java.lang.ClassNotFoundException: com.test.portlet.TestBackgroundTaskExecutor
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1714)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1559)
at com.liferay.portal.kernel.util.InstanceFactory.newInstance(InstanceFactory.java:52)
at com.liferay.portal.kernel.util.InstanceFactory.newInstance(InstanceFactory.java:27)
at com.liferay.portal.backgroundtask.messaging.BackgroundTaskMessageListener.doReceive(BackgroundTaskMessageListener.java:82)
at com.liferay.portal.kernel.messaging.BaseMessageListener.receive(BaseMessageListener.java:26)
at com.liferay.portal.kernel.messaging.InvokerMessageListener.receive(InvokerMessageListener.java:72)
at com.liferay.portal.kernel.messaging.ParallelDestination$1.run(ParallelDestination.java:69)
at com.liferay.portal.kernel.concurrent.ThreadPoolExecutor$WorkerTask._runTask(ThreadPoolExecutor.java:682)
at com.liferay.portal.kernel.concurrent.ThreadPoolExecutor$WorkerTask.run(ThreadPoolExecutor.java:593)
at java.lang.Thread.run(Thread.java:662)

Can i know is i am missing something?
Hi Raghu,

sorry for the late response, and please ignore it if you have been able to fix it since then.

So I think the problem is the caller part, where you have the BackgroundTaskLocalServiceUtil.addBackgroundTask(...) method call. Can I ask you the location of this code piece? If it is a an action within the portal, then the servletContextNames[0] = getClass().getClassLoader().toString(); returns the portal class loader and not your plugin's one, so that is why the class loader aware background task executor won't be able to find your executor class on the portal's class loader.

Could you please debug this code piece and let me know the result of the getClass().getClassLoader().toString(); expression?

thanks,
Daniel
I have developed a plugin to execute the job.
Both classes are in the plugin portlet.
At Debug point it returns the servlet context name is

[WebappClassLoader
context: /TestBackGroundTask-portlet
delegate: false
repositories:
/WEB-INF/classes/
----------> Parent Classloader:
org.apache.catalina.loader.StandardClassLoader@7e3bc473
]

I have tried to attach the plugin portlet. But no option to attach.
Hi Raghu,

I think you have the same issue what Michel had before. So you have to pass the context name to the BackgroundTask entity when you create it with the BTLSU.addBackgroundTask(..) this will force the API to change the classloader to you plugin's one when starting to execute the task.

The servlet context name is the name of your plugin's servlet context.

thanks,
Daniel
Hi Dániel,

thanks for the article.

I have a problem with passing complex objects via taskContextMap to the Executor.
When it tries to deserialize the parameters from taskContextMap, it throws a ClassNotFoundException, because the needed classes are in my portlet plugin context.

Did you have a similar problem?

Thanks
Tommaso
Hi Tommaso,

I haven't seen this problem yet. Could you please attach the stack trace of your exception?

thanks,
Daniel
Thank you for your response. I opened a new issue on jira (https://issues.liferay.com/browse/LPS-54220). There you can find the stack trace and the code recreating the problem.

Thank you
Tommaso
How do you stop a task that is in progress? I can't seem to figure it out.

Say I have an Exectutor that is running a loop in its execute() method. How do I send some sort of signal or change of status to the BackgroundTask and/or Executor to get it to exit the loop and shut down, etc.? I can delete the BackgroundTask, but it doesn't kill the background thread.

Can I use the BackgroundTaskStatusMessageTranslator for this, perhaps?
Hi Mike,

this is on our to do list, so hopefully we include a solution by the next release.

The idea is that we can't force to stop a thread (the thread of your background task executor) from another thread, but we can notify it to stop itself. So the possible solution will be to create a mechanism to be able to notify the BT executor to stop, but it is going to be your responsibility to react this event and stop and clean-up your executor state.

thanks,
Daniel
Thanks for the response. What you described is what I'm used to having to do. I just wanted to make sure I didn't overlook some kind of notification channel that I wasn't aware of. It should be fairly simple to come up with a quick solution.
Hi, Dániel,

First of all, good job. The API seems nice.

My question for you:
I am trying to find a way to "stop" the background process, I got everything else working fine. I tried with all sorts of things, like updating the BackgroundTask's status manually in the DB from one place, then reading it in the execute() method, but it does not see the values from the DB. I tried setting the volatile boolean flag in the BaseBackgroundTaskExecutor implementing class which contains the execute() method, and updating it from other place, but it also does not work for some reason (probably different classloaders and stuff).
The problem is that the layer of abstraction is provided with Background Task API, but no real stop method is, therefore I cannot access the thread to signalize it to stop itself.
Is there any way to "stop" the current task from executing?
Thanks in advance and keep up the good work!

Boris
Hi Boris,

first of all I am really happy you like the framework and thanks for your feedback!

What you mentioned is on our to do list for a long time now, so hopefully we can include a solution as soon as one of the upcoming releases.

The reason why we don't have any API support for this operation is that we can't really force to stop a thread (the thread of your background task executor) from another thread (the framework's one), but we can notify it to stop itself. So the code of the executor should listen this signal and stop itself, which would be a very poor solution API wise, so we wanted to give a more sophisticated one, like a listener or something that frequently checks for the signal and stops the execution after a proper clean-up, maybe an event listeners support as well to let the developer to react the termination, etc..

So it needs a lot of preparation from our side. For now I can only suggest to follow this approach, and after getting the executor thread of your BTE, call its interrupt() method, and check the isInterrupted() method from the executor frequently to make it stop.

thanks,
Daniel
Hi Daniel,

First of all thanks for writing this blog, it is really helpful. I tried to create a background task for exporting file. But over here i am sending some custom message in my Message Translator


@Override
public void translate(BackgroundTaskStatus backgroundTaskStatus,
Message message) {
System.out.println("This is called.....");
backgroundTaskStatus.setAttribute("xy", "XYZ testing");
}

and in jsp I had written below code, but that is printing null only :-

<%
BackgroundTaskStatus backgroundTaskStatus = BackgroundTaskStatusRegistryUtil.getBackgroundTaskStatus(backgroundTask.getBackgroundTaskId());
String stagedModelName = (String)backgroundTaskStatus.getAttribute("xy");
%>
Testing : - <%=stagedModelName %>

So here my question is how the value is updated dynamically if process is completed.

Waiting for your reply.

Thanks...