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.io.IOException; 019import java.util.Optional; 020 021import org.apache.avalon.framework.activity.Initializable; 022import org.apache.avalon.framework.service.ServiceException; 023import org.apache.avalon.framework.service.ServiceManager; 024import org.apache.commons.lang3.StringUtils; 025import org.quartz.JobExecutionContext; 026 027import org.ametys.core.schedule.progression.ContainerProgressionTracker; 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.core.util.mail.SendMailHelper.MailBuilder; 033import org.ametys.plugins.core.impl.schedule.AbstractStaticSchedulable; 034import org.ametys.plugins.core.user.UserHelper; 035import org.ametys.runtime.config.Config; 036import org.ametys.runtime.i18n.I18nizableText; 037 038import jakarta.mail.MessagingException; 039 040/** 041 * Abstract schedulable that send an email at the end of the execution 042 * 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 043 * If this user has no email address and there is an error during the execution, an email is sent to the system administrator 044 */ 045public abstract class AbstractSendingMailSchedulable extends AbstractStaticSchedulable implements Initializable 046{ 047 /** The utils for i18n */ 048 protected I18nUtils _i18nUtils; 049 /** Mail sender */ 050 protected String _mailSender; 051 /** Sys admin mail */ 052 protected String _sysadminMail; 053 /** Current user provider */ 054 protected CurrentUserProvider _currentUserProvider; 055 /** User helper */ 056 protected UserHelper _userHelper; 057 058 @Override 059 public void service(ServiceManager manager) throws ServiceException 060 { 061 super.service(manager); 062 _i18nUtils = (I18nUtils) manager.lookup(I18nUtils.ROLE); 063 _currentUserProvider = (CurrentUserProvider) manager.lookup(CurrentUserProvider.ROLE); 064 _userHelper = (UserHelper) manager.lookup(UserHelper.ROLE); 065 } 066 067 public void initialize() throws Exception 068 { 069 _mailSender = Config.getInstance().getValue("smtp.mail.from"); 070 _sysadminMail = Config.getInstance().getValue("smtp.mail.sysadminto"); 071 } 072 073 @Override 074 public void execute(JobExecutionContext context, ContainerProgressionTracker progressionTracker) throws Exception 075 { 076 Optional<String> recipient = _getRecipient(context); 077 078 I18nizableText mailSubject = null; 079 String mailBody = null; 080 try 081 { 082 _doExecute(context, progressionTracker); 083 084 mailSubject = _getSuccessMailSubject(context); 085 mailBody = _getSuccessMailBody(context); 086 } 087 catch (Exception e) 088 { 089 if (recipient.isEmpty()) 090 { 091 recipient = Optional.ofNullable(_sysadminMail) 092 .filter(StringUtils::isNotEmpty); 093 } 094 095 mailSubject = _getErrorMailSubject(context); 096 mailBody = _getErrorMailBody(context, e); 097 098 throw e; 099 } 100 finally 101 { 102 if (recipient.isPresent() && StringUtils.isNotEmpty(mailBody)) 103 { 104 _sendMail(mailSubject, mailBody, recipient.get(), context); 105 } 106 } 107 } 108 109 /** 110 * Executes the schedulable. 111 * @param context the context 112 * @param progressionTracker The progression tracker 113 * @throws Exception if an error occurred 114 */ 115 protected abstract void _doExecute(JobExecutionContext context, ContainerProgressionTracker progressionTracker) throws Exception; 116 117 /** 118 * Retrieves the optional recipient of the mail 119 * @param context the context 120 * @return the optional recipient of the mail 121 */ 122 protected Optional<String> _getRecipient(JobExecutionContext context) 123 { 124 return Optional.of(_currentUserProvider) 125 .map(CurrentUserProvider::getUser) 126 .map(_userManager::getUser) 127 .map(User::getEmail) 128 .filter(StringUtils::isNotEmpty); 129 } 130 131 /** 132 * Determines if the mail body is in HTML 133 * @param context the context 134 * @return <code>true</code> if the mail body is in HTML, <code>false</code> otherwise 135 * @throws Exception If an error occurs while retrieving if mail body should be HTML 136 */ 137 protected boolean _isMailBodyInHTML(JobExecutionContext context) throws Exception 138 { 139 return false; 140 } 141 142 /** 143 * Retrieves the subject of the success mail 144 * @param context the context 145 * @return the subject of the success mail 146 * @throws Exception If an error occurs while building the mail subject 147 */ 148 protected abstract I18nizableText _getSuccessMailSubject(JobExecutionContext context) throws Exception; 149 150 /** 151 * Retrieves the body of the success mail 152 * @param context the context 153 * @return the body of the success mail 154 * @throws Exception If an error occurs while building the mail body 155 */ 156 protected abstract String _getSuccessMailBody(JobExecutionContext context) throws Exception; 157 158 /** 159 * Retrieves the subject of the error mail 160 * @param context the context 161 * @return the subject of the error mail 162 * @throws Exception If an error occurs while building the mail subject 163 */ 164 protected abstract I18nizableText _getErrorMailSubject(JobExecutionContext context) throws Exception; 165 166 /** 167 * Retrieves the body of the error mail 168 * @param context the context 169 * @param throwable the error 170 * @return the body of the error mail 171 * @throws Exception If an error occurs while building the mail body 172 */ 173 protected abstract String _getErrorMailBody(JobExecutionContext context, Throwable throwable) throws Exception; 174 175 /** 176 * Send an email 177 * @param subject the email's subject 178 * @param body the email's body (to HTML or text format depending on {@link #_isMailBodyInHTML(JobExecutionContext)} 179 * @param recipient the recipient address 180 * @param context the context 181 */ 182 protected void _sendMail (I18nizableText subject, String body, String recipient, JobExecutionContext context) 183 { 184 try 185 { 186 MailBuilder mailBuilder = SendMailHelper.newMail() 187 .withSubject(_i18nUtils.translate(subject)) 188 .withRecipient(recipient) 189 .withSender(_mailSender); 190 191 if (_isMailBodyInHTML(context)) 192 { 193 mailBuilder.withHTMLBody(body); 194 } 195 else 196 { 197 mailBuilder.withTextBody(body); 198 } 199 200 mailBuilder.sendMail(); 201 } 202 catch (MessagingException | IOException e) 203 { 204 if (getLogger().isWarnEnabled()) 205 { 206 getLogger().warn("Unable to send the e-mail '" + subject + "' to '" + recipient + "'", e); 207 } 208 } 209 catch (Exception e) 210 { 211 getLogger().error("An unknown error has occured while sending the mail.", e); 212 } 213 } 214}