Motivation
CronJobs provide a means for performing business logic at particular times and intervals. For example,
an administrator might want to perform an inventory every Sunday at midnight,
or notify the web administrator every hour between 9 and 5 from Monday to Saturday of the peak loads on the servers.
Our cronjob should run on a daily basis and send eMails containing a ranking-list.
If you are lucky enough to have an SMTP server that you can use, you can set up the details as below.
If you don't, the Cronjob will just echo the text to the command prompt.
Setting up eMail with gmail
We'll configure here a sample gmail SMTP server. You can create an account to be used with this training (http://mail.google.com).
config/local.properties
mail.from=cuppy-no-reply@hybris.de
mail.replyto=cuppy-no-reply@hybris.de
mail.smtp.server=smtp.gmail.com
mail.smtp.port=587
mail.smtp.user=YOUR_GMAIL_ACCOUNT
mail.smtp.password=YOUR_GMAIL_PASSWORD
mail.smtp.starttls.enable=true
Defining the Job
Create a new java class SendRankingJob in the package de/hybris/platform/cuppytrail/jobs:
de/hybris/platform/cuppytrail/jobs/SendRankingJob.java/*
* [y] hybris Platform
*
* Copyright (c) 2000-2011 hybris AG
* All rights reserved.
*
* This software is the confidential and proprietary information of hybris
* ("Confidential Information"). You shall not disclose such Confidential
* Information and shall use it only in accordance with the terms of the
* license agreement you entered into with hybris.
*
*
*/
package de.hybris.platform.cuppytrail.jobs;
import de.hybris.platform.cronjob.enums.CronJobResult;
import de.hybris.platform.cronjob.enums.CronJobStatus;
import de.hybris.platform.cronjob.model.CronJobModel;
import de.hybris.platform.cuppy.model.PlayerModel;
import de.hybris.platform.cuppy.services.MailService;
import de.hybris.platform.cuppy.services.PlayerService;
import de.hybris.platform.cuppy.services.RankingData;
import de.hybris.platform.servicelayer.cronjob.AbstractJobPerformable;
import de.hybris.platform.servicelayer.cronjob.PerformResult;
import java.util.List;
import org.apache.log4j.Logger;
public class SendRankingJob extends AbstractJobPerformable<CronJobModel>
{
private static final Logger LOG = Logger.getLogger(SendRankingJob.class);
private PlayerService playerService;
private MailService mailService;
@Override
public PerformResult perform(final CronJobModel cronJob)
{
LOG.info("Sending ranking mails");
final List<RankingData> rankings = playerService.getRankings();
if (rankings.isEmpty())
{
LOG.info("No competitions have changed, skipping send of ranking mails");
return new PerformResult(CronJobResult.SUCCESS, CronJobStatus.FINISHED);
}
for (final PlayerModel player : playerService.getAllPlayers())
{
final List<RankingData> playerRankings = playerService.filterRankingsForPlayer(rankings, player);
if (!playerRankings.isEmpty() && player.isSendNewsletter())
{
mailService.sendRankingMail(player, playerRankings);
}
}
return new PerformResult(CronJobResult.SUCCESS, CronJobStatus.FINISHED);
}
public void setPlayerService(final PlayerService playerService)
{
this.playerService = playerService;
}
public void setMailService(final MailService trailMailService)
{
this.mailService = trailMailService;
}
}
The new JobPerformable has to be defined as a Spring bean in cuppytrail-spring.xml.
Add the following line at the bottom of the file, but inside the beans-tag:
resources/cuppytrail-spring.xml
<bean id="sendRankingJob" class="de.hybris.platform.cuppytrail.jobs.SendRankingJob" autowire="byName"/>
Rebuild the hybris Platform by calling ant
in the $/{HYBRIS_BIN_DIR}/platform
directory.
Run a system update with only essential data checked - during the phase of essential data creation,
for each Spring definition of a class implementing the JobPerformable
interface, a ServicelayerJob instance gets created and the code attribute of the job is set to the name of the Spring bean.
You can check in the FlexibleSearch console: http://localhost:9001/console/flexsearch that the new item was created by executing the following query:
select {code} from {servicelayerjob} where {code} = 'sendRankingJob'
Create the CronJob and the Trigger
To create the CronJob and the Trigger, you can either:
-
You can go in the hybris Admin Console to the Console tab select ImpEx Import and execute the following impex-script there by clicking on the Import Content button
INSERT_UPDATE CronJob; code[unique=true];job(code);singleExecutable;sessionLanguage(isocode)
;sendRankingCronJob;sendRankingJob;false;de
INSERT_UPDATE Trigger;cronjob(code)[unique=true];cronExpression
#% afterEach: impex.getLastImportedItem().setActivationTime(new Date());
; sendRankingCronJob; 0 0 0 * *
A cron expression is a string comprised of 6 or 7 fields separated by white space. Fields can contain any of the allowed values, along with various combinations of allowed special characters for that field.
---
-
Quartz cron trigger is used, see Quartz Scheduler for more details.
Any changes you are making for testing can easily be redeployed or reexecuted.
- OR create the file resources/impex/essentialdataJobs.impex with the same content. Further changes are only taken into account after a server-restart and a system update (with only essential data checked). Changes made to resources after being loaded as classloader-resources are not visible.
l10NService.getLocalizedString("mail.registration.subject", new Object[]{ player.getUid() })
mail.registration.subject=Player {0} has registered
=======
DefaultMailService.java
/**
*
*/
package de.hybris.platform.cuppy.services.impl;
import de.hybris.platform.commons.model.renderer.RendererTemplateModel;
import de.hybris.platform.commons.renderer.RendererService;
import de.hybris.platform.commons.renderer.exceptions.RendererException;
import de.hybris.platform.cuppy.model.MatchModel;
import de.hybris.platform.cuppy.model.NewsModel;
import de.hybris.platform.cuppy.model.PlayerModel;
import de.hybris.platform.cuppy.services.MailService;
import de.hybris.platform.cuppy.services.RankingData;
import de.hybris.platform.cuppy.services.SingletonScopedComponent;
import de.hybris.platform.servicelayer.i18n.FormatFactory;
import de.hybris.platform.servicelayer.i18n.I18NService;
import de.hybris.platform.servicelayer.i18n.L10NService;
import de.hybris.platform.servicelayer.session.SessionExecutionBody;
import de.hybris.platform.servicelayer.session.SessionService;
import de.hybris.platform.util.Config;
import java.io.StringWriter;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.MailException;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.mail.javamail.MimeMessagePreparator;
/**
* @author andreas.thaler
*
*/
@SingletonScopedComponent(value = "mailService")
public class DefaultMailService implements MailService, InitializingBean
{
private final static Logger LOG = Logger.getLogger(DefaultMailService.class);
private String domain;
private String fromAddress;
private String replyToAddress;
@Autowired
private JavaMailSender mailSender;
@Autowired
private SessionService sessionService;
@Autowired
private I18NService i18nService;
@Autowired
private FormatFactory formatFactory;
@Autowired
private RendererService rendererService;
@Autowired
private L10NService l10NService;
@Override
public void afterPropertiesSet()
{
domain = Config.getParameter("cuppy.domain");
fromAddress = Config.getParameter("mail.from");
replyToAddress = Config.getParameter("mail.replyto");
if (domain == null || domain.isEmpty() || fromAddress == null || fromAddress.isEmpty() || replyToAddress == null
|| replyToAddress.isEmpty())
{
throw new IllegalStateException(
"Can not start mail service, please configure properties 'cuppy.domain','mail.from' and 'mail.replyto'");
}
}
@Override
public void sendConfirmationMail(final PlayerModel player)
{
final MailPreparator preparer = new MailPreparator()
{
@Override
public void prepare(final MimeMessageHelper message) throws MessagingException
{
message.setSubject(l10NService.getLocalizedString("mail.confirmation.subject"));
message.setText(l10NService.getLocalizedString("mail.confirmation.body", new Object[]
{ player.getName(), "http://" + domain + "/index.zul", player.getUid() }));
}
};
send(preparer, player);
}
@Override
public void sendRegistrationMail(final PlayerModel player, final List<PlayerModel> admins)
{
for (final PlayerModel admin : admins)
{
final MailPreparator preparer = new MailPreparator()
{
@Override
public void prepare(final MimeMessageHelper message) throws MessagingException
{
message.setSubject(l10NService.getLocalizedString("mail.registration.subject", new Object[]
{ player.getUid() }));
message.setText(l10NService.getLocalizedString("mail.registration.body", new Object[]
{
admin.getName(),
"http://" + domain + "/index.zul?persp=cuppy.perspective.cuppy&events=activation&act-item="
+ player.getPk().toString() }));
}
};
send(preparer, admin);
}
}
@Override
public void sendNewsletter(final NewsModel news, final List<PlayerModel> players)
{
for (final PlayerModel player : players)
{
if (player.isSendNewsletter())
{
final MailPreparator preparer = new MailPreparator()
{
@Override
public void prepare(final MimeMessageHelper message) throws MessagingException
{
message.setSubject(l10NService.getLocalizedString("mail.news.subject"));
message.setText("<html><body>" + l10NService.getLocalizedString("mail.news.text", new Object[]
{ StringEscapeUtils.escapeHtml(player.getName()), news.getContent() }) + "</body></html>", true);
}
};
send(preparer, player);
}
}
}
@Override
public void sendReminder(final List<MatchModel> matches, final PlayerModel player)
{
final MailPreparator preparer = new MailPreparator()
{
@Override
public void prepare(final MimeMessageHelper message) throws MessagingException
{
final DateFormat dateFormat = formatFactory.createDateTimeFormat(DateFormat.MEDIUM, DateFormat.MEDIUM);
final List<MatchReminderHelper> matchReminders = new ArrayList<MatchReminderHelper>();
for (final MatchModel match : matches)
{
final String name = match.getGroup().getCompetition().getName();
final String kickOffTime = dateFormat.format(match.getDate());
final String home = match.getHomeTeam().getName();
final String guest = match.getGuestTeam().getName();
final MatchReminderHelper reminder = new MatchReminderHelper(name, kickOffTime, home, guest);
matchReminders.add(reminder);
}
final ReminderContext reminderContext = new ReminderContext(player, matchReminders);
final StringWriter mailMessage = new StringWriter();
final RendererTemplateModel reminderTemplate = rendererService.getRendererTemplateForCode("reminder");
rendererService.render(reminderTemplate, reminderContext, mailMessage);
message.setSubject(l10NService.getLocalizedString("mail.reminder.subject"));
message.setText(mailMessage.toString(), true);
}
};
send(preparer, player);
}
@Override
public void sendNewPassword(final PlayerModel player, final String newPassword)
{
final MailPreparator preparer = new MailPreparator()
{
@Override
public void prepare(final MimeMessageHelper message) throws MessagingException
{
message.setSubject(l10NService.getLocalizedString("mail.newpassword.subject"));
message.setText(l10NService.getLocalizedString("mail.newpassword.body", new Object[]
{ player.getName(), "http://" + domain + "/index.zul", player.getUid(), newPassword }));
}
};
send(preparer, player);
}
private String getMailExceptionMessage(final MailException exception)
{
final StringBuilder result = new StringBuilder(exception.getMessage());
if (exception.getCause() != null)
{
final Throwable cause = exception.getCause();
result.append(": ").append(exception.getMessage());
if (cause.getCause() != null)
{
result.append(": ").append(cause.getMessage());
}
}
return result.toString();
}
protected void send(final MailPreparator preparer, final PlayerModel player)
{
sessionService.executeInLocalView(new SessionExecutionBody()
{
@Override
public void executeWithoutResult()
{
i18nService.setLocalizationFallbackEnabled(true);
if (player.getSessionLanguage() != null)
{
i18nService.setCurrentLocale(new Locale(player.getSessionLanguage().getIsocode()));
}
final MimeMessagePreparator preparator = new MimeMessagePreparator()
{
@Override
public void prepare(final MimeMessage mimeMessage) throws Exception //NOPMD
{
final MimeMessageHelper message = new MimeMessageHelper(mimeMessage, "UTF-8");
message.setTo(player.getEMail());
message.setFrom(fromAddress);
message.setReplyTo(replyToAddress);
preparer.prepare(message);
}
};
try
{
mailSender.send(preparator);
}
catch (final MailException e)
{
//log it and go on
LOG.error("Can not send mail to " + player.getUid() + " - " + player.getEMail() + ": "
+ getMailExceptionMessage(e));
}
}
});
}
@Override
public void sendRankingMail(final PlayerModel player, final List<RankingData> rankings)
{
try
{
final MailPreparator preparer = new MailPreparator()
{
@Override
public void prepare(final MimeMessageHelper message) throws MessagingException
{
message.setSubject(l10NService.getLocalizedString("mail.ranking.subject"));
final RendererTemplateModel template = rendererService.getRendererTemplateForCode("rankingMail");
final StringWriter renderedText = new StringWriter();
rendererService.render(template, new RankingMailContext(rankings, player), renderedText);
message.setText(renderedText.getBuffer().toString(), true);
}
};
send(preparer, player);
}
catch (final RendererException e)
{
LOG.error("Error while rendering ranking mail for " + player.getUid() + ", skipping send of mail", e);
}
}
protected interface MailPreparator
{
void prepare(MimeMessageHelper message) throws Exception; //NOPMD
}
public void setMailSender(final JavaMailSender mailSender)
{
this.mailSender = mailSender;
}
}