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
- <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
- 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
- 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
- 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
- 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
- <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
- $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
- 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
- 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.
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
- 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
How to write a cronjob to remove log files?
Hi
I have a requirement to create a dynamic cronjob that executes a script and the job needs two custom attributes,
daysOld and MediaReport
daysOld attribute is used to create a delta report of the extract where time between current date and the modified date should be *daysOld* attribute defined in the cronjob
*MediaReport* is the list of extract that generates after executing the script. I need to attach the extract explicitly to the cronjob.
Do you have any suggestions on how to approach this?
Check below article on dynamic cronjob
http://javainsimpleway.com/dynamic-cron-job-cron-job-scripting/
Hi KB,
All your tutorials are really best! and I appreciate your efforts.
Can you write a tutorial on checkout customization?
Thanks in advance!
i think , in trigger step5
INSERT_UPDATE Trigger;cronjob(code)[unique=true];active;cronExpression
;productsRemovalCronJob;true;0 15 10 * * ?
we should give spring id (productsRemovalJob ).
Please correct me if i am wrong.
Hi KB,
i think we need to import below impex, after 4th step.
otherwise 5th step will not import, because spring bean id will not find.
INSERT_UPDATE ServicelayerJob;code[unique=true];springId[unique=true]
;productsRemovalJob;productsRemovalJob
This is not mandatory, it will be created when we do update system
How to do convert shell script code in to cron job in which csv file move from one directory to another directory.
You need to write a cron job which does it, there is no method which converts shell script to cron job in hybris.
In Step 2, for ‘Create a class which acts as a Job by extending AbstractJobPerformable’
Two questions:
Where is this file placed?
What is the file type? .java?
I’m having issue starting the server and I wanted to check my location and file type.
Thanks!
in step 4 at the time running the impex i am getting the error
,,,,unknown attribute ‘sites’ in header ‘INSERT_UPDATE ProductsRemovalCronJob
Have you done system update after making cron job entry in items.xml ?
I am facing the same issue, even ran update. any suggestions ??
I am running version 6.0
Have you added extends=”CronJob” in items.xml for your custom cron job ?
If you have not added then there is a chance that this error can come.
If you have added it then do one thing. check CronJobModel and see whether “sites” attribute is available or not.
sites attribute is not available
It means they have removed it in 6.0, Please see if they have added any alternate attribute for the same , If not remove that attribute from the impex and run it.
yes Sites attribute is not there, we removed it
Thanks for your solution and immediate response
You are welcome !!
thanks KB for the response
Can you tell me how to trigger a cron job just once through impex?
how to write a cron job to retrive the data from hybris db?
Above cron job also retrieving the product data from hybris DB.
Based on your requirement, you need to write the appropriate query.
hey, very well explained.
As part of my first hybris project i was given a cronjob to write,
wiki was not much of a help when compared to your blog.
thank you 🙂
Thank you !!