Configurable Scheduled Task (Cronjob) in Liferay DXP

Configurable Scheduled Task 

Overview

This article will explain and give you an example of configurable scheduled task. It may be enabled or disabled on-the-fly, cronjob expression for the task may be also changed dynamically.

Example

This is an example of scheduled task, which uses custom OSGi configuration:
______________________________________________________________________________
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.messaging.BaseMessageListener;
import com.liferay.portal.kernel.messaging.DestinationNames;
import com.liferay.portal.kernel.messaging.Message;
import com.liferay.portal.kernel.scheduler.*;
import org.osgi.service.component.annotations.*;

import java.util.Date;
import java.util.Map;

@Component(
        configurationPid = MyConfigurationKeys.MY_CONFIGURATION_ID, //todo 1: - set configuration ID
        immediate = true,
        property = {
        },
        service = BaseMessageListener.class
)
public class MyCronJob extends BaseMessageListener {

    private static final String CRON_EXPRESSION_DEFAULT = "0 * * * * ?";
    private static final boolean CRON_ENABLED_DEFAULT = false;

    @Activate
    @Modified
    protected void activate(Map<String, Object> properties) throws SchedulerException {
        String cronExpresion = CRON_EXPRESSION_DEFAULT;
        boolean cronEnabled = CRON_ENABLED_DEFAULT;

        //todo 2: - use your configuration util
        MyConfiguration myConfiguration = MyConfigurationUtil.getMyConfiguration();
        if (myConfiguration != null) {
            cronEnabled = myConfiguration.cronEnabled();
            cronExpresion = myConfiguration.cronExpression();
        }
        if (cronEnabled) {
            //Cronjob enabled
            String listenerClass = getClass().getName();
            Trigger jobTrigger = _triggerFactory.createTrigger(listenerClass, listenerClass, new Date(), null, cronExpresion);
            _schedulerEntryImpl = new SchedulerEntryImpl(getClass().getName(), jobTrigger);
            if (_initialized) {
                deactivate();
            }
            _schedulerEngineHelper.register(this, _schedulerEntryImpl, DestinationNames.SCHEDULER_DISPATCH);
            _log.debug("Scheduled task registered: " + cronExpresion);
            _initialized = true;
        } else {
            //Cronjob disabled
            unregister();
        }
    }

    @Override
    protected void doReceive(Message message) throws Exception {
        _myService.myAction();   // todo 3 - invoke your service
    }

    /* deactivate: Called when OSGi is deactivating the component */
    @Deactivate
    protected void deactivate() {
        unregister();
    }

    private void unregister() {
        // if we previously were initialized
        if (_initialized) {
            // un-schedule the job so it is cleaned up
            try {
                _schedulerEngineHelper.unschedule(_schedulerEntryImpl, getStorageType());
            } catch (SchedulerException se) {
                if (_log.isWarnEnabled()) {
                    _log.warn("Unable to unschedule trigger", se);
                }
            }
            // unregister this listener
            _schedulerEngineHelper.unregister(this);
        }
        // clear the initialized flag
        _initialized = false;
    }

    protected StorageType getStorageType() {
        if (_schedulerEntryImpl instanceof StorageTypeAware) {
            return ((StorageTypeAware) _schedulerEntryImpl).getStorageType();
        }
        return StorageType.MEMORY_CLUSTERED;
    }


    private volatile boolean _initialized;
    private SchedulerEntryImpl _schedulerEntryImpl = null;

    @Reference
    private TriggerFactory _triggerFactory;
    @Reference
    private SchedulerEngineHelper _schedulerEngineHelper;
    @Reference
    private MyService _myService;  // todo 4 - inject your service

    private static final Log _log = LogFactoryUtil.getLog(MyCronJob.class);
}
______________________________________________________________________________

TODOs here:
  • 1 - set you own configuration ID (check more detail in Custom OSGi Settings article;
  • 2 - use your configuration util wrapper for your OSGi configuration;
  • 3 - use your service with business-logic you need to invoke in the cronjob.
  • 4 - inject the service you need;


Important notes:
  • configurationPid should be specified to listen to changes in OSGi configuration;
  • Cronjob class should extend BaseMessageListener class;
  • Both @Activate and @Modified annotations should be used to re-activate component after OSGi configuration properties modified;
  • doReceive method is the one invoked by scheduled task, business-logic should be placed here;
  • deactivate method should have unregister scheduled listener logic and annotated with @Deactivate annotation.

Required dependencies:

dependencies {     
    compileOnly group: "com.liferay.portal", name: "com.liferay.portal.kernel"
    compileOnly group: "com.liferay.portal", name: "com.liferay.util.taglib"
    compileOnly group: "javax.portlet", name: "portlet-api"
    compileOnly group: "javax.servlet", name: "javax.servlet-api"
    compileOnly group: "jstl", name: "jstl"
    compileOnly group: "org.osgi", name: "org.osgi.service.component.annotations"
    compileOnly project(":modules:my-configuration")
    compileOnly project(":modules:my-service")
}

(use your own configuration and service modules)

Enjoy 😉

Comments

Popular posts from this blog

Liferay Search Container Example

Liferay DXP - max upload file size

Liferay Keycloak integration