Sunday, August 1, 2021

Liferay Dynamic Schedule Jobs Implementation

Liferay is providing scheduler API to create schedule job in Liferay Portal Applications. Liferay internally uses the Quartz scheduler engine. Liferay also uses the Message Bus implementation with scheduler API.

 





 Software Stack


 

Liferay-ce-portal-7.4.1-ga2

 

 

 


Below Article will provide more details on Liferay Scheduler

 


http://www.liferaysavvy.com/2021/07/working-with-liferay-scheduler.html

 



Liferay API is using Message Bus for every scheduler job and following article will provide more detail about Liferay Message Bus.

 


http://www.liferaysavvy.com/2021/07/working-with-liferay-message-bus.html

 


http://www.liferaysavvy.com/2021/07/liferay-message-bus-implementation.html

 



Following are the steps to implement Dynamic schedule jobs in Liferay

 

  • Create Message Bus Destination
  • Register Listener with Destination
  • Create Scheduler job and tag to Destination

 



Create Message Bus Destination

 

Create destination have following steps in OSGi module

 

  • Create Destination Configuration
  • Create Destination
  • Manage the Destination Object

 


Create Destination Configuration


Message Bus API is providing DestinationConfiguration class to create destination configuration. We can create different types of destination as it specified as below.

 


Parallel Destination Configuration


 

DestinationConfiguration destinationConfiguration =

              new DestinationConfiguration(

                   DestinationConfiguration.DESTINATION_TYPE_PARALLEL,LiferayMessageBusPortletKeys.DESTINATION_PARALLEL);

 

 

 

 


Serial Destination Configuration


 

DestinationConfiguration destinationConfiguration =

              new DestinationConfiguration(

                   DestinationConfiguration.DESTINATION_TYPE_SERIAL,LiferayMessageBusPortletKeys.DESTINATION_SERIAL);

 

 


Synchronous Destination Configuration


 

DestinationConfiguration destinationConfiguration =

              new DestinationConfiguration(

                   DestinationConfiguration.DESTINATION_TYPE_SYNCHRONOUS,LiferayMessageBusPortletKeys.DESTINATION_SYNCHRONOUS);

 

 

 

 



Create Destination


DestinationFactory will create destination based on configuration

 


 

Destination destination = _destinationFactory.createDestination(

              destinationConfiguration);

 

 



 

Register Destination as OSGi Service



ServiceRegistration<Destination> is used to register destination as OSGi service.


 

_destinationServiceRegistration = _bundleContext.registerService(

              Destination.class, destination, destinationProperties);

         _log.info("Destination is registred with Service Regisration ..");

 

 

 

 


Manage the Destination Object



We have to manage destination object so that it can deregister when bundle is deactivated.

 


 

Dictionary<String, Object> destinationProperties =

              HashMapDictionaryBuilder.<String, Object>put(

                  "destination.name", destination.getName()).build();

 

 

 


Destroy Destination


 

@Deactivate

     protected void deactivate() {

         if (_destinationServiceRegistration != null) {

              Destination destination = _bundleContext.getService(

                   _destinationServiceRegistration.getReference());

 

              _destinationServiceRegistration.unregister();

 

              destination.destroy();

         }

 

 

 


Setting Thread Pool for destination



 

destinationConfiguration.setMaximumQueueSize(_MAXIMUM_QUEUE_SIZE);

destinationConfiguration.setWorkersCoreSize(_CORE_SIZE);

destinationConfiguration.setWorkersMaxSize(_MAX_SIZE);

 

 

 


Rejection Handler to Handle Failed Messages

 


 

RejectedExecutionHandler rejectedExecutionHandler =

              new ThreadPoolExecutor.CallerRunsPolicy() {

 

                  @Override

                  public void rejectedExecution(

                       Runnable runnable, ThreadPoolExecutor threadPoolExecutor) {

 

     if (_log.isWarnEnabled()) {

         _log.warn("The current thread will handle the request " + "because the rules engine's task queue is at " +"its maximum capacity");

       }

 

super.rejectedExecution(runnable, threadPoolExecutor);

 }

 

};

 

destinationConfiguration.setRejectedExecutionHandler(rejectedExecutionHandler);

 

 

 

 


Register Listener with Destination


Listener should implement MessageListener interface. We have to implement receive(..) method and there we implement our business logic. Listeners are registered with destination to received messages from senders.

 

 


There are different ways to register listener below is the one of the ways.

 


Automatic Registration


 

Create MessageListener component and pass destination name as property so that it will be register with destination automatically when component is create.



 

@Component (

         immediate = true,

         property = {"destination.name=liferaysavvy/synchronous-destination"},

         service = MessageListener.class

     )

public class AutomaticRegisteredSynchronousMessageListener implements MessageListener {

     @Override

     public void receive(Message message) {

 

         try {

              _log.info("Message::"+message);

             

         }

         catch (Exception e) {

              e.printStackTrace();

         }

        

     }

 

    

 

     private static final Log _log = LogFactoryUtil.getLog(

          AutomaticRegisteredSynchronousMessageListener.class);

 

}

 

 



Create Scheduler job and tag to Destination

 


SchedulerEngineHelperUtil is class will provide methods to create schedule Jobs. We can also use “SchedulerEngineHelper” OSGi reference to create same schedule jobs.

 


Following are API method to schedule Job in Liferay

 


public static void schedule(
      Trigger trigger
, StorageType storageType, String description,
     
String destinationName, Message message, int exceptionsMaxSize)
  
throws SchedulerException

 


public static void schedule(
      Trigger trigger
, StorageType storageType, String description,
      
String destinationName, Object payload, int exceptionsMaxSize)
  
throws SchedulerException

 


Following are the important Parameters

 


Trigger


Its cron trigger Object. Trigger factory will be used to create trigger object. It is required CRON expression and it should be valid quartz cron expression.

 


http://www.quartz-scheduler.org/documentation/quartz-2.3.0/tutorials/crontrigger.html

 

Storage Type


Its scheduler storage type like Memory or Persisted.

 


Destination Name


Its message bus destination where scheduler engine will send messages.

 


Message


It's JSON object to carry required data from Message Bus to Listener.

 


Liferay 7.x onwards Liferay scheduler API is providing the Timezone while creating Trigger and it will resolve the Daylight time zone issue for scheduled jobs.

 


 

public Trigger createTrigger(

                    String jobName, String groupName, Date startDate, Date endDate,

                    String cronExpression, TimeZone timeZone);

 

 


Example



//Trigger trigger = TriggerFactoryUtil.createTrigger(jobName,groupName,cronExpression);

         Trigger trigger = _triggerFactory.createTrigger(jobName,groupName,null,null,cron,TimeZone.getDefault());

 

 

@Reference

private TriggerFactory _triggerFactory;

      

 

 


Creating Schedule



 

//SchedulerEngineHelperUtil.schedule(trigger, StorageType.PERSISTED, description,destinationName, message, exceptionsMaxSize)

         _schedulerEngineHelper.schedule(trigger, StorageType.PERSISTED, description,destinationName, message, exceptionsMaxSize);

 

 

@Reference

private SchedulerEngineHelper _schedulerEngineHelper;

 

 



Deploy and Run Example OSGi Module

 


Find OSGi module from the below Git repository and it will demonstrate, create dynamic schedule jobs.

 


https://github.com/LiferaySavvy/liferay-scheduler-example

 

 


Clone the project and import into your Liferay workspace.

 


Build and Deploy OSGi module with Gradle tasks.

 


Add Widget to Liferay Page

 




 

Access “Create Dynamic Schedule Jobs” UI  screen and create job

 




Once job is created, Job details will be stored in Quartz Database tables.

 




 


 

In the console logs, every 2 min Scheduler engine will trigger the job and send message to Message Bus. Other end listener will be receiving the message and print in the console logs.

 


 

 


2021-08-01 15:02:00.026 INFO  [liferaysavvy/parallel-destination-10][AutomaticRegisteredParellelMessageListener:34] Message::{destinationName=liferaysavvy/parallel-destination, response=null, responseDestinationName=null, responseId=null, payload=null, values={GROUP_NAME=Dynamic, companyId=20100, data=TwoMinJob:Dynamic, groupId=0, DESTINATION_NAME=liferaysavvy/parallel-destination, EXCEPTIONS_MAX_SIZE=10, JOB_STATE=com.liferay.portal.kernel.scheduler.JobState@31b87667, STORAGE_TYPE=PERSISTED, JOB_NAME=TwoMinJob}}

 

2021-08-01 15:04:00.025 INFO  [liferaysavvy/parallel-destination-11][AutomaticRegisteredParellelMessageListener:34] Message::{destinationName=liferaysavvy/parallel-destination, response=null, responseDestinationName=null, responseId=null, payload=null, values={GROUP_NAME=Dynamic, companyId=20100, data=TwoMinJob:Dynamic, groupId=0, DESTINATION_NAME=liferaysavvy/parallel-destination, EXCEPTIONS_MAX_SIZE=10, JOB_STATE=com.liferay.portal.kernel.scheduler.JobState@2dd02e8e, STORAGE_TYPE=PERSISTED, JOB_NAME=TwoMinJob}}

 


 

 

This is how we can create dynamic schedule job and every schedule job must be tagged with  destination.


 

Destinations are not persisted so it must be created when server startup or bundle is  activated. That’s reason we implemented destination creation as part of bundle activation. Schedule jobs are persisted as we are selected Persisted storage type.




Author

10 comments :

  1. This article content is really unique and amazing. This article really helpful and explained very well. So I am really thankful to you for sharing keep it up..

    ReplyDelete
  2. Thanks for sharing such a useful blog. Really! This Blog is very informative for us which contains a lot of information about the Writing. I like this post.

    ReplyDelete
  3. Very informative post! There is a lot of information here that can help any business get started with a successful social networking campaign.

    ReplyDelete
  4. Thanks for the post. I think as a business owner, I think about the best WordPress form plugin

    ReplyDelete
  5. Mobile phone is the great invention of modern science. With help of it the communication system developed much more. Handy Zubehör(mobile phone shop) can assist you for give you the best experience of gadgets and also with their services.

    ReplyDelete
  6. This is a wonderful post, I am very happy to read this article. Thanks for giving us this useful information. RJtalents, Best Modeling and Talents Management Agency In Mumbai, India.

    Talent management agency in Mumbai
    Talent management agency in Bangalore

    ReplyDelete
  7. Thanks for sharing such a useful blog. Really! This Blog is very informative for us which contains a lot of information about the Writing. I like this post.
    you can buy watches for girls here:
    https://leyjao.pk/fashion/best-womens-fashion/classic-watches/

    ReplyDelete
  8. This comment has been removed by a blog administrator.

    ReplyDelete
  9. All information you wish to understand about digital marketing must be researched on a computer. When you are having a computer or laptop problem, the experts are here to help. Register to take advantage of their Laptop reparatur Frankfurt service and receive a first-time discount of up to 15%.

    ReplyDelete
  10. In Bangladesh, a variety of engine oil brands, along with mineral, semi-synthetic, and synthetic oils, are to be had in exceptional grades to healthy the particular wishes of motors and riding conditions. Here at Bike Engine Oil in BD.

    ReplyDelete

Recent Posts

Recent Posts Widget

Popular Posts