001/*
002 *  Copyright 2020 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.cms.schedule;
017
018import java.util.Optional;
019
020import javax.mail.MessagingException;
021
022import org.apache.avalon.framework.activity.Initializable;
023import org.apache.avalon.framework.service.ServiceException;
024import org.apache.avalon.framework.service.ServiceManager;
025import org.apache.commons.lang3.StringUtils;
026import org.quartz.JobExecutionContext;
027
028import org.ametys.core.user.CurrentUserProvider;
029import org.ametys.core.user.User;
030import org.ametys.core.util.I18nUtils;
031import org.ametys.core.util.mail.SendMailHelper;
032import org.ametys.plugins.core.impl.schedule.AbstractStaticSchedulable;
033import org.ametys.plugins.core.user.UserHelper;
034import org.ametys.runtime.config.Config;
035import org.ametys.runtime.i18n.I18nizableText;
036
037/**
038 * Abstract schedulable that send an email at the end of the execution
039 * By default, the email is sent to the user that launched the schedulable. This behavior can be overridden thanks to the {@link #_getRecipient(JobExecutionContext)} method
040 * If this user has no email address and there is an error during the execution, an email is sent to the system administrator
041 */
042public abstract class AbstractSendingMailSchedulable extends AbstractStaticSchedulable implements Initializable
043{
044    /** The utils for i18n */
045    protected I18nUtils _i18nUtils;
046    /** Mail sender */
047    protected String _mailSender;
048    /** Sys admin mail */
049    protected String _sysadminMail;
050    /** Current user provider */
051    protected CurrentUserProvider _currentUserProvider;
052    /** User helper */
053    protected UserHelper _userHelper;
054    
055    @Override
056    public void service(ServiceManager manager) throws ServiceException
057    {
058        super.service(manager);
059        _i18nUtils = (I18nUtils) manager.lookup(I18nUtils.ROLE);
060        _currentUserProvider = (CurrentUserProvider) manager.lookup(CurrentUserProvider.ROLE);
061        _userHelper = (UserHelper) manager.lookup(UserHelper.ROLE);
062    }
063
064    public void initialize() throws Exception
065    {
066        _mailSender = Config.getInstance().getValue("smtp.mail.from");
067        _sysadminMail = Config.getInstance().getValue("smtp.mail.sysadminto");
068    }
069
070    @Override
071    public void execute(JobExecutionContext context) throws Exception
072    {
073        Optional<String> recipient = _getRecipient(context);
074        
075        I18nizableText mailSubject = null;
076        I18nizableText mailBody = null;
077        try
078        {
079            _doExecute(context);
080            
081            mailSubject = _getSuccessMailSubject(context);
082            mailBody = _getSuccessMailBody(context);
083        }
084        catch (Exception e)
085        {
086            if (recipient.isEmpty())
087            {
088                recipient = Optional.ofNullable(_sysadminMail)
089                        .filter(StringUtils::isNotEmpty);
090            }
091            
092            mailSubject = _getErrorMailSubject(context);
093            mailBody = _getErrorMailBody(context, e);
094
095            throw e;
096        }
097        finally
098        {
099            if (recipient.isPresent())
100            {
101                _sendMail(mailSubject, mailBody, recipient.get(), context);
102            }
103        }
104    }
105    
106    /**
107     * Executes the schedulable.
108     * @param context the context
109     * @throws Exception if an error occurred
110     */
111    protected abstract void _doExecute(JobExecutionContext context) throws Exception;
112    
113    /**
114     * Retrieves the optional recipient of the mail
115     * @param context the context
116     * @return the optional recipient of the mail
117     */
118    protected Optional<String> _getRecipient(JobExecutionContext context)
119    {
120        return Optional.of(_currentUserProvider)
121            .map(CurrentUserProvider::getUser)
122            .map(_userManager::getUser)
123            .map(User::getEmail)
124            .filter(StringUtils::isNotEmpty);
125    }
126    
127    /**
128     * Determines if the mail body is in HTML
129     * @param context the context
130     * @return <code>true</code> if the mail body is in HTML, <code>false</code> otherwise
131     * @throws Exception If an error occurs while retrieving if mail body should be HTML
132     */
133    protected boolean _isMailBodyInHTML(JobExecutionContext context) throws Exception
134    {
135        return false;
136    }
137    
138    /**
139     * Retrieves the subject of the success mail
140     * @param context the context
141     * @return the subject of the success mail
142     * @throws Exception If an error occurs while building the mail subject
143     */
144    protected abstract I18nizableText _getSuccessMailSubject(JobExecutionContext context) throws Exception;
145    
146    /**
147     * Retrieves the body of the success mail
148     * @param context the context
149     * @return the body of the success mail
150     * @throws Exception If an error occurs while building the mail body
151     */
152    protected abstract I18nizableText _getSuccessMailBody(JobExecutionContext context) throws Exception;
153    
154    /**
155     * Retrieves the subject of the error mail
156     * @param context the context
157     * @return the subject of the error mail
158     * @throws Exception If an error occurs while building the mail subject
159     */
160    protected abstract I18nizableText _getErrorMailSubject(JobExecutionContext context) throws Exception;
161    
162    /**
163     * Retrieves the body of the error mail
164     * @param context the context
165     * @param throwable the error
166     * @return the body of the error mail
167     * @throws Exception If an error occurs while building the mail body
168     */
169    protected abstract I18nizableText _getErrorMailBody(JobExecutionContext context, Throwable throwable) throws Exception;
170    
171    /**
172     * Send an email
173     * @param subject the email's subject
174     * @param body the email's body
175     * @param recipient the recipient address
176     * @param context the context
177     */
178    protected void _sendMail (I18nizableText subject, I18nizableText body, String recipient, JobExecutionContext context)
179    {
180        try
181        {
182            if (_isMailBodyInHTML(context))
183            {
184                SendMailHelper.sendMail(_i18nUtils.translate(subject), _i18nUtils.translate(body), null, recipient, _mailSender);
185            }
186            else
187            {
188                SendMailHelper.sendMail(_i18nUtils.translate(subject), null, _i18nUtils.translate(body), recipient, _mailSender);
189            }
190        }
191        catch (MessagingException e)
192        {
193            if (getLogger().isWarnEnabled())
194            {
195                getLogger().warn("Unable to send the e-mail '" + subject  + "' to '" + recipient + "'", e);
196            }
197        }
198        catch (Exception e)
199        {
200            getLogger().error("An unknown error has occured while sending the mail.", e);
201        }
202    }
203}