CronJob Real time example


Requirement:

We need to run some task which should remove the products whose price is not defined and it is X days old in the system.

Now our business logic is to remove the products if its price is not defined and it is X no of days old.

Steps for the business logic


Load the Products which are X days old
Check if the product has price from the above product list
If the price is not defined , delete the product

Step 1


Since we need to add new configuration property called X days as dynamic value to the Cron Job, we need to define a new CronJob Item type

Define new Cron Job item type in items.xml as below
hybris\bin\custom\training\trainingcore\resources\trainingcore-items.xml

1
2
3
4
5
6
7
8
9
10
11
12
<itemtype code="ProductsRemovalCronJob" extends="CronJob" jaloclass="de.hybris.platform.cronjob.jalo.ProductsRemovalCronJob"
                  autocreate="true" generate="true">
            <attributes>
                <attribute qualifier="xDaysOld" type="int">
                    <modifiers read="true" write="true" optional="false"/>
                    <defaultvalue>Integer.valueOf(10)</defaultvalue>
                    <description>All Products older than this value in days and whose price is not defined will be removed
                    </description>
                    <persistence type="property"/>
                </attribute>
            </attributes>
        </itemtype>
<itemtype code="ProductsRemovalCronJob" extends="CronJob" jaloclass="de.hybris.platform.cronjob.jalo.ProductsRemovalCronJob"
                  autocreate="true" generate="true">
            <attributes>
                <attribute qualifier="xDaysOld" type="int">
                    <modifiers read="true" write="true" optional="false"/>
                    <defaultvalue>Integer.valueOf(10)</defaultvalue>
                    <description>All Products older than this value in days and whose price is not defined will be removed
                    </description>
                    <persistence type="property"/>
                </attribute>
            </attributes>
        </itemtype>

Defined a new Cron Job item type ProductsRemovalCronJob which extends CronJob item type
So all the attributes of CronJob item type are also inherited to this new Cron Job item type.

Do ant all and refresh the platform folder to check ProductsRemovalCronJobModel.java is generated

Step 2


Create a class which acts as a Job by extending AbstractJobPerformable class and override the perform() method to have our business logic

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Created the class inside our custom core extension as below
 
package org.training.core.jobs;
 
import de.hybris.platform.cronjob.enums.CronJobResult;
import de.hybris.platform.cronjob.enums.CronJobStatus;
import de.hybris.platform.cronjob.model.ProductsRemovalCronJobModel;
import de.hybris.platform.servicelayer.cronjob.AbstractJobPerformable;
import de.hybris.platform.servicelayer.cronjob.PerformResult;
 
 
public class ProductsRemovalJob extends AbstractJobPerformable<ProductsRemovalCronJobModel>
{
    @Override
    public PerformResult perform(final ProductsRemovalCronJobModel productsRemovalCronJobModel)
    {
 
        return new PerformResult(CronJobResult.SUCCESS, CronJobStatus.FINISHED);
    }
 
}
Created the class inside our custom core extension as below

package org.training.core.jobs;

import de.hybris.platform.cronjob.enums.CronJobResult;
import de.hybris.platform.cronjob.enums.CronJobStatus;
import de.hybris.platform.cronjob.model.ProductsRemovalCronJobModel;
import de.hybris.platform.servicelayer.cronjob.AbstractJobPerformable;
import de.hybris.platform.servicelayer.cronjob.PerformResult;


public class ProductsRemovalJob extends AbstractJobPerformable<ProductsRemovalCronJobModel>
{
	@Override
	public PerformResult perform(final ProductsRemovalCronJobModel productsRemovalCronJobModel)
	{

		return new PerformResult(CronJobResult.SUCCESS, CronJobStatus.FINISHED);
	}

}

This is a class which does not have any business logic written for the Job to perform.

Let’s define the Business logic for the same as below
hybris\hybris\bin\custom\training\trainingcore\src\org\training\core\jobs\ProductsRemovalJob.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package org.training.core.jobs;
 
import de.hybris.platform.core.model.product.ProductModel;
import de.hybris.platform.cronjob.enums.CronJobResult;
import de.hybris.platform.cronjob.enums.CronJobStatus;
import de.hybris.platform.cronjob.model.ProductsRemovalCronJobModel;
import de.hybris.platform.servicelayer.cronjob.AbstractJobPerformable;
import de.hybris.platform.servicelayer.cronjob.PerformResult;
import de.hybris.platform.servicelayer.exceptions.ModelRemovalException;
import de.hybris.platform.servicelayer.model.ModelService;
import de.hybris.platform.site.BaseSiteService;
import de.hybris.platform.solrfacetsearch.config.FacetSearchConfig;
import de.hybris.platform.solrfacetsearch.config.FacetSearchConfigService;
import de.hybris.platform.solrfacetsearch.config.exceptions.FacetConfigServiceException;
import de.hybris.platform.solrfacetsearch.indexer.IndexerService;
import de.hybris.platform.solrfacetsearch.indexer.exceptions.IndexerException;
import de.hybris.platform.solrfacetsearch.model.config.SolrFacetSearchConfigModel;
import de.hybris.platform.tx.Transaction;
 
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
 
import org.apache.log4j.Logger;
import org.springframework.util.CollectionUtils;
import org.training.core.dao.CustomProductsDAO;
 
 
public class ProductsRemovalJob extends AbstractJobPerformable<ProductsRemovalCronJobModel>
{
    public static final String SITE_UID = "apparel-uk";
    private CustomProductsDAO customProductsDao;
    private ModelService modelService;
    private IndexerService indexerService;
    private FacetSearchConfigService facetSearchConfigService;
    private BaseSiteService baseSiteService;
 
    private final static Logger LOG = Logger.getLogger(ProductsRemovalJob.class.getName());
 
    @Override
    public PerformResult perform(final ProductsRemovalCronJobModel productsRemovalCronJobModel)
    {
        final int noOfDaysOldToRemove = productsRemovalCronJobModel.getXDaysOld();
        final Calendar cal = Calendar.getInstance();
        cal.add(Calendar.DAY_OF_MONTH, -noOfDaysOldToRemove);
        final Date oldDate = cal.getTime();
        final List<ProductModel> productModelListToBeDeleted = new ArrayList<>();
        final List<ProductModel> productModelList = customProductsDao.findAllProductsOlderThanSpecifiedDays(oldDate);
        LOG.debug("Products older than specified days size:" + productModelList.size());
        for (final ProductModel productModel : productModelList)
        {
            if (CollectionUtils.isEmpty(productModel.getEurope1Prices()))
            {
                productModelListToBeDeleted.add(productModel);
            }
        }
 
        if (!CollectionUtils.isEmpty(productModelListToBeDeleted))
        {
            Transaction tx = null;
            try
            {
                tx = Transaction.current();
                tx.begin();
                getModelService().removeAll(productModelListToBeDeleted);
                tx.commit();
            }
            catch (final ModelRemovalException e)
            {
                if (null != tx)
                {
                    tx.rollback();
                }
                LOG.error("Could not remove the product list -->" + e);
            }
        }
 
        try
        {
            final SolrFacetSearchConfigModel facetSearchConfigModel = baseSiteService.getBaseSiteForUID(SITE_UID)
                    .getSolrFacetSearchConfiguration();
            final FacetSearchConfig facetSearchConfig = facetSearchConfigService.getConfiguration(facetSearchConfigModel.getName());
            indexerService.performFullIndex(facetSearchConfig);
        }
        catch (final FacetConfigServiceException | IndexerException e)
        {
            LOG.error("Could not index the products -->" + e);
        }
 
        return new PerformResult(CronJobResult.SUCCESS, CronJobStatus.FINISHED);
    }
 
    public CustomProductsDAO getCustomProductsDao()
    {
        return customProductsDao;
    }
 
    public void setCustomProductsDao(final CustomProductsDAO customProductsDao)
    {
        this.customProductsDao = customProductsDao;
    }
 
    public ModelService getModelService()
    {
        return modelService;
    }
 
    @Override
    public void setModelService(final ModelService modelService)
    {
 
        this.modelService = modelService;
    }
 
    
    public IndexerService getIndexerService()
    {
        return indexerService;
    }
 
    
    public void setIndexerService(final IndexerService indexerService)
    {
        this.indexerService = indexerService;
    }
 
    
    public FacetSearchConfigService getFacetSearchConfigService()
    {
        return facetSearchConfigService;
    }
 
    
    public void setFacetSearchConfigService(final FacetSearchConfigService facetSearchConfigService)
    {
        this.facetSearchConfigService = facetSearchConfigService;
    }
 
    public BaseSiteService getBaseSiteService()
    {
        return baseSiteService;
    }
 
    public void setBaseSiteService(final BaseSiteService baseSiteService)
    {
        this.baseSiteService = baseSiteService;
    }
 
}
package org.training.core.jobs;

import de.hybris.platform.core.model.product.ProductModel;
import de.hybris.platform.cronjob.enums.CronJobResult;
import de.hybris.platform.cronjob.enums.CronJobStatus;
import de.hybris.platform.cronjob.model.ProductsRemovalCronJobModel;
import de.hybris.platform.servicelayer.cronjob.AbstractJobPerformable;
import de.hybris.platform.servicelayer.cronjob.PerformResult;
import de.hybris.platform.servicelayer.exceptions.ModelRemovalException;
import de.hybris.platform.servicelayer.model.ModelService;
import de.hybris.platform.site.BaseSiteService;
import de.hybris.platform.solrfacetsearch.config.FacetSearchConfig;
import de.hybris.platform.solrfacetsearch.config.FacetSearchConfigService;
import de.hybris.platform.solrfacetsearch.config.exceptions.FacetConfigServiceException;
import de.hybris.platform.solrfacetsearch.indexer.IndexerService;
import de.hybris.platform.solrfacetsearch.indexer.exceptions.IndexerException;
import de.hybris.platform.solrfacetsearch.model.config.SolrFacetSearchConfigModel;
import de.hybris.platform.tx.Transaction;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import org.apache.log4j.Logger;
import org.springframework.util.CollectionUtils;
import org.training.core.dao.CustomProductsDAO;


public class ProductsRemovalJob extends AbstractJobPerformable<ProductsRemovalCronJobModel>
{
	public static final String SITE_UID = "apparel-uk";
	private CustomProductsDAO customProductsDao;
	private ModelService modelService;
	private IndexerService indexerService;
	private FacetSearchConfigService facetSearchConfigService;
	private BaseSiteService baseSiteService;

	private final static Logger LOG = Logger.getLogger(ProductsRemovalJob.class.getName());

	@Override
	public PerformResult perform(final ProductsRemovalCronJobModel productsRemovalCronJobModel)
	{
		final int noOfDaysOldToRemove = productsRemovalCronJobModel.getXDaysOld();
		final Calendar cal = Calendar.getInstance();
		cal.add(Calendar.DAY_OF_MONTH, -noOfDaysOldToRemove);
		final Date oldDate = cal.getTime();
		final List<ProductModel> productModelListToBeDeleted = new ArrayList<>();
		final List<ProductModel> productModelList = customProductsDao.findAllProductsOlderThanSpecifiedDays(oldDate);
		LOG.debug("Products older than specified days size:" + productModelList.size());
		for (final ProductModel productModel : productModelList)
		{
			if (CollectionUtils.isEmpty(productModel.getEurope1Prices()))
			{
				productModelListToBeDeleted.add(productModel);
			}
		}

		if (!CollectionUtils.isEmpty(productModelListToBeDeleted))
		{
			Transaction tx = null;
			try
			{
				tx = Transaction.current();
				tx.begin();
				getModelService().removeAll(productModelListToBeDeleted);
				tx.commit();
			}
			catch (final ModelRemovalException e)
			{
				if (null != tx)
				{
					tx.rollback();
				}
				LOG.error("Could not remove the product list -->" + e);
			}
		}

		try
		{
			final SolrFacetSearchConfigModel facetSearchConfigModel = baseSiteService.getBaseSiteForUID(SITE_UID)
					.getSolrFacetSearchConfiguration();
			final FacetSearchConfig facetSearchConfig = facetSearchConfigService.getConfiguration(facetSearchConfigModel.getName());
			indexerService.performFullIndex(facetSearchConfig);
		}
		catch (final FacetConfigServiceException | IndexerException e)
		{
			LOG.error("Could not index the products -->" + e);
		}

		return new PerformResult(CronJobResult.SUCCESS, CronJobStatus.FINISHED);
	}

	public CustomProductsDAO getCustomProductsDao()
	{
		return customProductsDao;
	}

	public void setCustomProductsDao(final CustomProductsDAO customProductsDao)
	{
		this.customProductsDao = customProductsDao;
	}

	public ModelService getModelService()
	{
		return modelService;
	}

	@Override
	public void setModelService(final ModelService modelService)
	{

		this.modelService = modelService;
	}

	
	public IndexerService getIndexerService()
	{
		return indexerService;
	}

	
	public void setIndexerService(final IndexerService indexerService)
	{
		this.indexerService = indexerService;
	}

	
	public FacetSearchConfigService getFacetSearchConfigService()
	{
		return facetSearchConfigService;
	}

	
	public void setFacetSearchConfigService(final FacetSearchConfigService facetSearchConfigService)
	{
		this.facetSearchConfigService = facetSearchConfigService;
	}

	public BaseSiteService getBaseSiteService()
	{
		return baseSiteService;
	}

	public void setBaseSiteService(final BaseSiteService baseSiteService)
	{
		this.baseSiteService = baseSiteService;
	}

}

We have written the business logic which gets all the products older than specified days

Then we are iterating the product List and checking each product whether they have price row or not, If price row is not defined then we are adding such products to a list of deleting products.
then we are removing all such products.

After that we are calling the Solr full indexing to index only those products which have the price.

Since we need to define the query to get all the products older than specified days, we will create a new DAO Implementation class and interface as below

CustomProductsDAO.java
hybris\hybris\bin\custom\training\trainingcore\src\org\training\core\dao\CustomProductsDAO.java

1
2
3
4
5
6
7
8
9
10
package org.training.core.dao;
 
import de.hybris.platform.core.model.product.ProductModel;
import java.util.Date;
import java.util.List;
 
public interface CustomProductsDAO
{
    public List<ProductModel> findAllProductsOlderThanSpecifiedDays(final Date oldDate);
}
package org.training.core.dao;

import de.hybris.platform.core.model.product.ProductModel;
import java.util.Date;
import java.util.List;

public interface CustomProductsDAO
{
	public List<ProductModel> findAllProductsOlderThanSpecifiedDays(final Date oldDate);
}

CustomProductsDAOImpl.java
hybris\hybris\bin\custom\training\trainingcore\src\org\training\core\dao\impl\CustomProductsDAOImpl.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package org.training.core.dao.impl;
 
import de.hybris.platform.core.model.product.ProductModel;
import de.hybris.platform.servicelayer.search.FlexibleSearchQuery;
import de.hybris.platform.servicelayer.search.FlexibleSearchService;
import de.hybris.platform.servicelayer.search.SearchResult;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.training.core.dao.CustomProductsDAO;
 
 
public class CustomProductsDAOImpl implements CustomProductsDAO
{
 
    private FlexibleSearchService flexibleSearchService;
 
    @Override
    public List<ProductModel> findAllProductsOlderThanSpecifiedDays(final Date oldDate)
    {
        final StringBuilder query = new StringBuilder("SELECT {pk} FROM {Product} WHERE {creationtime}<=?oldDate");
    final Map<String, Object> params = new HashMap<String, Object>();
        params.put("oldDate", oldDate);
 
        final FlexibleSearchQuery searchQuery = new FlexibleSearchQuery(query.toString());
        searchQuery.addQueryParameters(params);
        searchQuery.setResultClassList(Collections.singletonList(ProductModel.class));
        final SearchResult searchResult = getFlexibleSearchService().search(searchQuery);
        return searchResult.getResult();
    }
 
    
    public FlexibleSearchService getFlexibleSearchService()
    {
        return flexibleSearchService;
    }
 
    
    public void setFlexibleSearchService(final FlexibleSearchService flexibleSearchService)
    {
        this.flexibleSearchService = flexibleSearchService;
    }
 
}
package org.training.core.dao.impl;

import de.hybris.platform.core.model.product.ProductModel;
import de.hybris.platform.servicelayer.search.FlexibleSearchQuery;
import de.hybris.platform.servicelayer.search.FlexibleSearchService;
import de.hybris.platform.servicelayer.search.SearchResult;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.training.core.dao.CustomProductsDAO;


public class CustomProductsDAOImpl implements CustomProductsDAO
{

	private FlexibleSearchService flexibleSearchService;

	@Override
	public List<ProductModel> findAllProductsOlderThanSpecifiedDays(final Date oldDate)
	{
		final StringBuilder query = new StringBuilder("SELECT {pk} FROM {Product} WHERE {creationtime}<=?oldDate");
	final Map<String, Object> params = new HashMap<String, Object>();
		params.put("oldDate", oldDate);

		final FlexibleSearchQuery searchQuery = new FlexibleSearchQuery(query.toString());
		searchQuery.addQueryParameters(params);
		searchQuery.setResultClassList(Collections.singletonList(ProductModel.class));
		final SearchResult searchResult = getFlexibleSearchService().search(searchQuery);
		return searchResult.getResult();
	}

	
	public FlexibleSearchService getFlexibleSearchService()
	{
		return flexibleSearchService;
	}

	
	public void setFlexibleSearchService(final FlexibleSearchService flexibleSearchService)
	{
		this.flexibleSearchService = flexibleSearchService;
	}

}


Step 3

Define the above classes as spring beans in the *-spring.xml file as below
hybris\bin\custom\training\trainingcore\resources\trainingcore-spring.xml

1
2
3
4
5
6
7
8
9
10
11
12
<bean id="customProductsDao" class="org.training.core.dao.impl.CustomProductsDAOImpl">
        <property name="flexibleSearchService" ref="flexibleSearchService"/>
    </bean>
    
    <bean id="productsRemovalJob" class="org.training.core.jobs.ProductsRemovalJob"
        parent="abstractJobPerformable">
        <property name="customProductsDao" ref="customProductsDao" />
        <property name="modelService" ref="modelService" />
        <property name="indexerService" ref="indexerService" />
        <property name="facetSearchConfigService" ref="facetSearchConfigService" />
        <property name="baseSiteService" ref="baseSiteService"/>
    </bean>
<bean id="customProductsDao" class="org.training.core.dao.impl.CustomProductsDAOImpl">
		<property name="flexibleSearchService" ref="flexibleSearchService"/>
	</bean>
	
	<bean id="productsRemovalJob" class="org.training.core.jobs.ProductsRemovalJob"
		parent="abstractJobPerformable">
		<property name="customProductsDao" ref="customProductsDao" />
		<property name="modelService" ref="modelService" />
		<property name="indexerService" ref="indexerService" />
		<property name="facetSearchConfigService" ref="facetSearchConfigService" />
		<property name="baseSiteService" ref="baseSiteService"/>
	</bean>


Step 4

Write an impex to create a cron job instance as below
hybris\bin\custom\training\trainingcore\resources\impex\essentialdataJobs.impex

1
2
3
$siteUid=apparel-uk
INSERT_UPDATE ProductsRemovalCronJob; code[unique=true];job(code);xDaysOld;sessionLanguage(isocode);sessionCurrency(isocode);sites(uid)[default=$siteUid]
;productsRemovalCronJob;productsRemovalJob;5;en;EUR
$siteUid=apparel-uk
INSERT_UPDATE ProductsRemovalCronJob; code[unique=true];job(code);xDaysOld;sessionLanguage(isocode);sessionCurrency(isocode);sites(uid)[default=$siteUid]
;productsRemovalCronJob;productsRemovalJob;5;en;EUR

We are specifying old days as 5 so that 5 days older products will be considered.


Step 5

Write a impex into the same file to create a Trigger instance as below

1
2
3
INSERT_UPDATE Trigger;cronjob(code)[unique=true];active;cronExpression
#Fires at 10:15 AM everyday
;productsRemovalCronJob;true;0 15 10 * * ?
INSERT_UPDATE Trigger;cronjob(code)[unique=true];active;cronExpression
#Fires at 10:15 AM everyday
;productsRemovalCronJob;true;0 15 10 * * ?

Look at the relation between CronJob,Job and Trigger here.

While inserting ProductsRemovalCronJob Cron Job we are giving the reference of Job which should be same as spring bean id of the Job.

While inserting Trigger, we are giving the reference of ProductsRemovalCronJob which should be same as code defined while inserting ProductsRemovalCronJob

Also we have used Cron Expression in the Trigger which schedules it to run every day at 10:15 AM


Step 6

Do Update System by selecting the extension where we defined our job
Now Cron Job will be executed at the scheduled time automatically.

Note:
After Updating the system,we can run the below query to check the ServiceLayerJob created for the Job that we defined as a spring bean

1
select * from {servicelayerjob} where {code} = 'productsRemovalJob'
select * from {servicelayerjob} where {code} = 'productsRemovalJob'

We should be able to see the Job details if everything is fine.


Step 7


Check the Products in HMC whose price not defined and they are X days old before Job runs

Search the same products after the Job runs successfully

We should not be able to see those products now.

See below screenshot which shows products only those which have prices and all other products have been deleted.

Products after cron job removal


How to Configure the CronJob manually ?

We can configure and run the cron job through HMC whenever we want to do it manually.

Steps for the same are as below

1)Log in to HMC
2)Go to System -> CronJobs
3)Type productsRemovalCronJob in Value Text Box for code then click on search
4)open productsRemovalCronJob
5) Go to Time Schedule tab ->set time in (Trigger text box) then save it.

If we want to start it immediately then Click Start CronJob now then our job will perform and display popup box with CronJob performed Result


How to execute the CronJob through Code rather than through Trigger ?

We can also execute the Cron Job through code rather than running it through Triggers as below

1
2
3
4
5
6
7
8
9
ProductsRemovalCronJobModel  productsRemovalCronJobModel =modelService.create(ProductsRemovalCronJobModel.class);
 
// assign Job to CronJob
ServicelayerJobModel  servicelayerJobModel = modelService.create(ServicelayerJobModel.class);
servicelayerJobModel.setActive(true);
servicelayerJobModel.setSpringId("productsRemovalJob");
productsRemovalCronJobModel.setJob(servicelayerJobModel);
modelService.save(productsRemovalCronJobModel);
cronJobService.performCronJob(productsRemovalCronJobModel);
ProductsRemovalCronJobModel  productsRemovalCronJobModel =modelService.create(ProductsRemovalCronJobModel.class);

// assign Job to CronJob
ServicelayerJobModel  servicelayerJobModel = modelService.create(ServicelayerJobModel.class);
servicelayerJobModel.setActive(true);
servicelayerJobModel.setSpringId("productsRemovalJob");
productsRemovalCronJobModel.setJob(servicelayerJobModel);
modelService.save(productsRemovalCronJobModel);
cronJobService.performCronJob(productsRemovalCronJobModel);

In the above code, we have written a code to call the Cron job through CronJobService’s performCronJob() method

About the Author

Karibasappa G C (KB)
Founder of javainsimpleway.com
I love Java and open source technologies and very much passionate about software development.
I like to share my knowledge with others especially on technology 🙂
I have given all the examples as simple as possible to understand for the beginners.
All the code posted on my blog is developed,compiled and tested in my development environment.
If you find any mistakes or bugs, Please drop an email to kb.knowledge.sharing@gmail.com

Connect with me on Facebook for more updates

Share this article on