From 6d6c792eb93f2f1d4801cae4a7c0146d6fd5800e Mon Sep 17 00:00:00 2001 From: srosse <none@none> Date: Wed, 1 Mar 2017 14:15:58 +0100 Subject: [PATCH] OO-2535: send the mails asynchronous --- .../org/olat/core/util/mail/MailManager.java | 9 +++++ .../core/util/mail/_spring/mailContext.xml | 10 ++++++ .../util/mail/manager/MailManagerImpl.java | 34 +++++++++++++++++++ .../org/olat/course/_spring/courseContext.xml | 2 +- .../iq/QTI21AssessmentRunController.java | 9 ++--- .../org/olat/ims/qti21/OutcomesListener.java | 3 +- .../ims/qti21/manager/QTI21ServiceImpl.java | 7 +--- .../qti21/model/InMemoryOutcomeListener.java | 3 +- .../ui/AssessmentEntryOutcomesListener.java | 11 +++--- .../ui/AssessmentTestDisplayController.java | 2 +- .../ui/QTI21AssessmentDetailsController.java | 4 +-- 11 files changed, 74 insertions(+), 20 deletions(-) diff --git a/src/main/java/org/olat/core/util/mail/MailManager.java b/src/main/java/org/olat/core/util/mail/MailManager.java index ef135be3d63..1504a049448 100644 --- a/src/main/java/org/olat/core/util/mail/MailManager.java +++ b/src/main/java/org/olat/core/util/mail/MailManager.java @@ -180,6 +180,15 @@ public interface MailManager { */ public MailerResult sendMessage(MailBundle... bundles); + /** + * Send the mail bundle asynchronous. The queue is in memory (for the moment) + * and a shut down of the queue will mean looses. + * + * @param bundles + * @return + */ + public void sendMessageAsync(MailBundle... bundles); + public MailerResult sendExternMessage(MailBundle bundle, MailerResult result, boolean useTemplate); public MimeMessage createMimeMessage(Address from, Address[] tos, Address[] ccs, Address[] bccs, String subject, String body, diff --git a/src/main/java/org/olat/core/util/mail/_spring/mailContext.xml b/src/main/java/org/olat/core/util/mail/_spring/mailContext.xml index d7e3f6950c0..b864cac3ad1 100644 --- a/src/main/java/org/olat/core/util/mail/_spring/mailContext.xml +++ b/src/main/java/org/olat/core/util/mail/_spring/mailContext.xml @@ -13,6 +13,16 @@ <property name="className" value="org.olat.core.util.mail.ui.SendDocumentsByEMailController"/> </bean> + <bean id="mailAsyncExecutorService" class="org.springframework.core.task.support.ExecutorServiceAdapter"> + <constructor-arg index="0" ref="mailAsyncExecutor" /> + </bean> + + <bean id="mailAsyncExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"> + <property name="corePoolSize" value="1" /> + <property name="maxPoolSize" value="1" /> + <property name="queueCapacity" value="2000" /> + </bean> + <!-- Mail admin. panel --> <bean class="org.olat.core.extensions.action.GenericActionExtension" id="sysadmin.menupoint.syscfg.mailcfg" init-method="initExtensionPoints"> <property name="order" value="7208" /> diff --git a/src/main/java/org/olat/core/util/mail/manager/MailManagerImpl.java b/src/main/java/org/olat/core/util/mail/manager/MailManagerImpl.java index de50b5560a4..fcdcf4db538 100644 --- a/src/main/java/org/olat/core/util/mail/manager/MailManagerImpl.java +++ b/src/main/java/org/olat/core/util/mail/manager/MailManagerImpl.java @@ -38,6 +38,8 @@ import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.RejectedExecutionException; import java.util.zip.Adler32; import javax.activation.DataHandler; @@ -71,12 +73,14 @@ import org.apache.velocity.exception.ResourceNotFoundException; import org.apache.velocity.runtime.RuntimeConstants; import org.olat.basesecurity.IdentityImpl; import org.olat.basesecurity.IdentityRef; +import org.olat.core.CoreSpringFactory; import org.olat.core.commons.persistence.DB; import org.olat.core.commons.services.notifications.NotificationsManager; import org.olat.core.commons.services.notifications.Publisher; import org.olat.core.commons.services.notifications.PublisherData; import org.olat.core.commons.services.notifications.Subscriber; import org.olat.core.commons.services.notifications.SubscriptionContext; +import org.olat.core.commons.services.taskexecutor.model.DBSecureRunnable; import org.olat.core.helpers.Settings; import org.olat.core.id.Identity; import org.olat.core.id.OLATResourceable; @@ -112,6 +116,7 @@ import org.olat.core.util.vfs.VFSLeaf; import org.olat.core.util.vfs.VFSManager; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; import com.sun.mail.smtp.SMTPMessage; @@ -138,6 +143,8 @@ public class MailManagerImpl implements MailManager, InitializingBean { @Autowired private DB dbInstance; + @Autowired @Qualifier("mailAsyncExecutorService") + private ExecutorService asyncExecutor; @Autowired private NotificationsManager notificationsManager; private final MailModule mailModule; @@ -673,6 +680,19 @@ public class MailManagerImpl implements MailManager, InitializingBean { return bundle; } + @Override + public void sendMessageAsync(MailBundle... bundles) { + try { + SendMail sendMail = new SendMail(bundles); + DBSecureRunnable command = new DBSecureRunnable(sendMail); + asyncExecutor.execute(command); + } catch (RejectedExecutionException e) { + log.error("Queue full, email lost", e); + } catch (Exception e) { + log.error("", e); + } + } + @Override public MailerResult sendMessage(MailBundle... bundles) { MailerResult result = new MailerResult(); @@ -1710,6 +1730,20 @@ public class MailManagerImpl implements MailManager, InitializingBean { } } + public static class SendMail implements Runnable { + + private final MailBundle[] bundles; + + public SendMail(MailBundle[] bundles) { + this.bundles = bundles; + } + + @Override + public void run() { + CoreSpringFactory.getImpl(MailManager.class).sendMessage(bundles); + } + } + private static class VFSDataSource implements DataSource { private final String name; diff --git a/src/main/java/org/olat/course/_spring/courseContext.xml b/src/main/java/org/olat/course/_spring/courseContext.xml index ef4ada5205b..4dd9f94ad41 100644 --- a/src/main/java/org/olat/course/_spring/courseContext.xml +++ b/src/main/java/org/olat/course/_spring/courseContext.xml @@ -24,7 +24,7 @@ <import resource="classpath:/org/olat/course/statistic/_spring/statisticContext.xml"/> <import resource="classpath:/org/olat/course/statistic/_spring/statisticsJobContext.xml"/> -<bean id="courseFactory" class="org.olat.course.CourseFactory" + <bean id="courseFactory" class="org.olat.course.CourseFactory" depends-on="businessGroupService,resourceManager,baseSecurityManager,glossaryManager"> <constructor-arg index="0" ref="coordinatorManager" /> <constructor-arg index="1" ref="repositoryManager" /> diff --git a/src/main/java/org/olat/course/nodes/iq/QTI21AssessmentRunController.java b/src/main/java/org/olat/course/nodes/iq/QTI21AssessmentRunController.java index cdd9eaa2053..776ce8a9241 100644 --- a/src/main/java/org/olat/course/nodes/iq/QTI21AssessmentRunController.java +++ b/src/main/java/org/olat/course/nodes/iq/QTI21AssessmentRunController.java @@ -563,15 +563,16 @@ public class QTI21AssessmentRunController extends BasicController implements Gen } @Override - public void decorateConfirmation(AssessmentTestSession candidateSession, DigitalSignatureOptions options, Locale locale) { - decorateCourseConfirmation(candidateSession, options, userCourseEnv.getCourseEnvironment(), courseNode, testEntry, locale); + public void decorateConfirmation(AssessmentTestSession candidateSession, DigitalSignatureOptions options, Date timestamp, Locale locale) { + decorateCourseConfirmation(candidateSession, options, userCourseEnv.getCourseEnvironment(), courseNode, testEntry, timestamp, locale); } public static void decorateCourseConfirmation(AssessmentTestSession candidateSession, DigitalSignatureOptions options, - CourseEnvironment courseEnv, CourseNode courseNode, RepositoryEntry testEntry, Locale locale) { + CourseEnvironment courseEnv, CourseNode courseNode, RepositoryEntry testEntry, Date timestamp, Locale locale) { MailBundle bundle = new MailBundle(); bundle.setToId(candidateSession.getIdentity()); String fullname = CoreSpringFactory.getImpl(UserManager.class).getUserDisplayName(candidateSession.getIdentity()); + Date assessedDate = candidateSession.getFinishTime() == null ? timestamp : candidateSession.getFinishTime(); String[] args = new String[] { courseEnv.getCourseTitle(), // {0} @@ -581,7 +582,7 @@ public class QTI21AssessmentRunController extends BasicController implements Gen testEntry.getDisplayname(), // {4} fullname, // {5} Formatter.getInstance(locale) - .formatDateAndTime(candidateSession.getFinishTime()) // {6} + .formatDateAndTime(assessedDate) // {6} }; Translator translator = Util.createPackageTranslator(QTI21AssessmentRunController.class, locale); diff --git a/src/main/java/org/olat/ims/qti21/OutcomesListener.java b/src/main/java/org/olat/ims/qti21/OutcomesListener.java index a19af703d34..2f242abb3d9 100644 --- a/src/main/java/org/olat/ims/qti21/OutcomesListener.java +++ b/src/main/java/org/olat/ims/qti21/OutcomesListener.java @@ -19,6 +19,7 @@ */ package org.olat.ims.qti21; +import java.util.Date; import java.util.Locale; import org.olat.ims.qti21.model.DigitalSignatureOptions; @@ -38,7 +39,7 @@ public interface OutcomesListener { * @param options * @param locale */ - public void decorateConfirmation(AssessmentTestSession candidateSession, DigitalSignatureOptions options, Locale locale); + public void decorateConfirmation(AssessmentTestSession candidateSession, DigitalSignatureOptions options, Date timestamp, Locale locale); /** * Update the outcomes. diff --git a/src/main/java/org/olat/ims/qti21/manager/QTI21ServiceImpl.java b/src/main/java/org/olat/ims/qti21/manager/QTI21ServiceImpl.java index c01a7d174e5..454fde42dfd 100644 --- a/src/main/java/org/olat/ims/qti21/manager/QTI21ServiceImpl.java +++ b/src/main/java/org/olat/ims/qti21/manager/QTI21ServiceImpl.java @@ -66,7 +66,6 @@ import org.olat.core.util.crypto.CryptoUtil; import org.olat.core.util.crypto.X509CertificatePrivateKeyPair; import org.olat.core.util.mail.MailBundle; import org.olat.core.util.mail.MailManager; -import org.olat.core.util.mail.MailerResult; import org.olat.core.util.xml.XMLDigitalSignatureUtil; import org.olat.core.util.xml.XStreamHelper; import org.olat.fileresource.FileResourceManager; @@ -656,12 +655,8 @@ public class QTI21ServiceImpl implements QTI21Service, UserDataDeletable, Initia List<File> attachments = new ArrayList<>(2); attachments.add(signatureFile); mail.getContent().setAttachments(attachments); - MailerResult result = mailManager.sendMessage(mail); - if(result.getReturnCode() != MailerResult.OK) { - log.error("Confirmation mail cannot be send"); - } + mailManager.sendMessageAsync(mail); } - } catch (Exception e) { log.error("", e); } diff --git a/src/main/java/org/olat/ims/qti21/model/InMemoryOutcomeListener.java b/src/main/java/org/olat/ims/qti21/model/InMemoryOutcomeListener.java index 7e0733a34de..a4832f3aa93 100644 --- a/src/main/java/org/olat/ims/qti21/model/InMemoryOutcomeListener.java +++ b/src/main/java/org/olat/ims/qti21/model/InMemoryOutcomeListener.java @@ -19,6 +19,7 @@ */ package org.olat.ims.qti21.model; +import java.util.Date; import java.util.Locale; import org.olat.ims.qti21.AssessmentTestSession; @@ -33,7 +34,7 @@ import org.olat.ims.qti21.OutcomesListener; public class InMemoryOutcomeListener implements OutcomesListener { @Override - public void decorateConfirmation(AssessmentTestSession candidateSession, DigitalSignatureOptions options, Locale locale) { + public void decorateConfirmation(AssessmentTestSession candidateSession, DigitalSignatureOptions options, Date timestamp, Locale locale) { //do nothing } diff --git a/src/main/java/org/olat/ims/qti21/ui/AssessmentEntryOutcomesListener.java b/src/main/java/org/olat/ims/qti21/ui/AssessmentEntryOutcomesListener.java index 0b4847bb0e2..93ecfed65f5 100644 --- a/src/main/java/org/olat/ims/qti21/ui/AssessmentEntryOutcomesListener.java +++ b/src/main/java/org/olat/ims/qti21/ui/AssessmentEntryOutcomesListener.java @@ -20,6 +20,7 @@ package org.olat.ims.qti21.ui; import java.math.BigDecimal; +import java.util.Date; import java.util.Locale; import java.util.concurrent.atomic.AtomicBoolean; @@ -65,14 +66,16 @@ public class AssessmentEntryOutcomesListener implements OutcomesListener { } @Override - public void decorateConfirmation(AssessmentTestSession candidateSession, DigitalSignatureOptions options, Locale locale) { - decorateResourceConfirmation(candidateSession, options, locale); + public void decorateConfirmation(AssessmentTestSession candidateSession, DigitalSignatureOptions options, Date timestamp, Locale locale) { + decorateResourceConfirmation(candidateSession, options, timestamp, locale); } - public static void decorateResourceConfirmation(AssessmentTestSession candidateSession, DigitalSignatureOptions options, Locale locale) { + public static void decorateResourceConfirmation(AssessmentTestSession candidateSession, DigitalSignatureOptions options, Date timestamp, Locale locale) { MailBundle bundle = new MailBundle(); bundle.setToId(candidateSession.getIdentity()); String fullname = CoreSpringFactory.getImpl(UserManager.class).getUserDisplayName(candidateSession.getIdentity()); + Date assessedDate = candidateSession.getFinishTime() == null ? timestamp : candidateSession.getFinishTime(); + Translator translator = Util.createPackageTranslator(QTI21RuntimeController.class, locale); RepositoryEntry entry = candidateSession.getRepositoryEntry(); @@ -85,7 +88,7 @@ public class AssessmentEntryOutcomesListener implements OutcomesListener { testEntry.getDisplayname(), // {4} fullname, // {5} Formatter.getInstance(locale) - .formatDateAndTime(candidateSession.getFinishTime()) // {6} + .formatDateAndTime(assessedDate) // {6} }; String subject = translator.translate("digital.signature.mail.subject", args); diff --git a/src/main/java/org/olat/ims/qti21/ui/AssessmentTestDisplayController.java b/src/main/java/org/olat/ims/qti21/ui/AssessmentTestDisplayController.java index 0a0ea3c46c9..b80ee94bcb8 100644 --- a/src/main/java/org/olat/ims/qti21/ui/AssessmentTestDisplayController.java +++ b/src/main/java/org/olat/ims/qti21/ui/AssessmentTestDisplayController.java @@ -1143,7 +1143,7 @@ public class AssessmentTestDisplayController extends BasicController implements boolean digitalSignature = deliveryOptions.isDigitalSignature() && qtiModule.isDigitalSignatureEnabled(); DigitalSignatureOptions options = new DigitalSignatureOptions(digitalSignature, sendMail, entry, testEntry); if(digitalSignature) { - outcomesListener.decorateConfirmation(candidateSession, options, getLocale()); + outcomesListener.decorateConfirmation(candidateSession, options, getCurrentRequestTimestamp(), getLocale()); } return options; } diff --git a/src/main/java/org/olat/ims/qti21/ui/QTI21AssessmentDetailsController.java b/src/main/java/org/olat/ims/qti21/ui/QTI21AssessmentDetailsController.java index 9b7888d46d9..3d677f09ee6 100644 --- a/src/main/java/org/olat/ims/qti21/ui/QTI21AssessmentDetailsController.java +++ b/src/main/java/org/olat/ims/qti21/ui/QTI21AssessmentDetailsController.java @@ -390,10 +390,10 @@ public class QTI21AssessmentDetailsController extends FormBasicController { DigitalSignatureOptions options = new DigitalSignatureOptions(digitalSignature, sendMail, entry, testEntry); if(digitalSignature) { if(courseNode == null) { - AssessmentEntryOutcomesListener.decorateResourceConfirmation(session, options, getLocale()); + AssessmentEntryOutcomesListener.decorateResourceConfirmation(session, options, null, getLocale()); } else { CourseEnvironment courseEnv = CourseFactory.loadCourse(entry).getCourseEnvironment(); - QTI21AssessmentRunController.decorateCourseConfirmation(session, options, courseEnv, courseNode, sessionTestEntry, getLocale()); + QTI21AssessmentRunController.decorateCourseConfirmation(session, options, courseEnv, courseNode, sessionTestEntry, null, getLocale()); } } return options; -- GitLab