PageRenderTime 7851ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/common/source/java/ee/webmedia/alfresco/email/service/EmailServiceImpl.java

https://bitbucket.org/smitdevel/delta
Java | 363 lines | 307 code | 45 blank | 11 comment | 59 complexity | 06ba28fbfd4d3b416813f13666ef49a4 MD5 | raw file
  1. package ee.webmedia.alfresco.email.service;
  2. import ee.webmedia.alfresco.app.AppConstants;
  3. import ee.webmedia.alfresco.common.service.GeneralService;
  4. import ee.webmedia.alfresco.common.web.BeanHelper;
  5. import ee.webmedia.alfresco.email.model.EmailAttachment;
  6. import ee.webmedia.alfresco.monitoring.MonitoredService;
  7. import ee.webmedia.alfresco.monitoring.MonitoringUtil;
  8. import ee.webmedia.alfresco.signature.service.DigiDoc4JSignatureService;
  9. import ee.webmedia.alfresco.signature.service.SignatureService;
  10. import ee.webmedia.alfresco.utils.FilenameUtil;
  11. import ee.webmedia.alfresco.utils.MimeUtil;
  12. import org.alfresco.error.AlfrescoRuntimeException;
  13. import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
  14. import org.alfresco.repo.transaction.TransactionListenerAdapter;
  15. import org.alfresco.service.cmr.model.FileFolderService;
  16. import org.alfresco.service.cmr.model.FileInfo;
  17. import org.alfresco.service.cmr.repository.ContentData;
  18. import org.alfresco.service.cmr.repository.MimetypeService;
  19. import org.alfresco.service.cmr.repository.NodeRef;
  20. import org.alfresco.util.TempFileProvider;
  21. import org.apache.commons.io.IOUtils;
  22. import org.apache.commons.io.output.CountingOutputStream;
  23. import org.apache.commons.io.output.NullOutputStream;
  24. import org.apache.commons.lang.StringUtils;
  25. import org.apache.commons.lang.time.FastDateFormat;
  26. import org.springframework.core.io.FileSystemResource;
  27. import org.springframework.core.io.InputStreamSource;
  28. import org.springframework.mail.javamail.JavaMailSender;
  29. import org.springframework.mail.javamail.MimeMessageHelper;
  30. import org.springframework.util.CollectionUtils;
  31. import javax.mail.Address;
  32. import javax.mail.internet.InternetAddress;
  33. import javax.mail.internet.MimeMessage;
  34. import java.io.*;
  35. import java.security.cert.X509Certificate;
  36. import java.util.*;
  37. import java.util.zip.DeflaterOutputStream;
  38. public class EmailServiceImpl implements EmailService {
  39. private static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(EmailServiceImpl.class);
  40. private static final FastDateFormat dateFormat = FastDateFormat.getInstance("yyyy-MM-dd-HH-mm-ss-SSSZ");
  41. private JavaMailSender mailService;
  42. private FileFolderService fileFolderService;
  43. private GeneralService generalService;
  44. private SignatureService _signatureService;
  45. private DigiDoc4JSignatureService _digiDoc4JSignatureService;
  46. private MimetypeService mimetypeService;
  47. private static String messageCopyFolder = null;
  48. // /// PUBLIC METHODS
  49. @Override
  50. public void sendEmail(List<String> toEmails, List<String> toNames, String fromEmail, String subject, String content, boolean isHtml, NodeRef document,
  51. List<EmailAttachment> attachments) throws EmailException {
  52. sendEmail(toEmails, toNames, null, null, fromEmail, subject, content, isHtml, document, attachments);
  53. }
  54. @Override
  55. public void sendEmail(List<String> toEmails, List<String> toNames, List<String> toBccEmails, List<String> toBccNames, String fromEmail, String subject,
  56. String content, boolean isHtml, NodeRef document, List<EmailAttachment> attachments) throws EmailException {
  57. long step0 = System.currentTimeMillis();
  58. if (CollectionUtils.isEmpty(toEmails) && CollectionUtils.isEmpty(toBccEmails)) {
  59. throw new EmailException("At least one of toEmails and toBccEmails is mandatory.");
  60. }
  61. if (fromEmail == null) {
  62. throw new EmailException("Parameter fromEmail is mandatory.");
  63. }
  64. if (subject == null) {
  65. throw new EmailException("Parameter subject is mandatory.");
  66. }
  67. if (content == null) {
  68. throw new EmailException("Parameter content is mandatory.");
  69. }
  70. // Avoid NPE
  71. if (toEmails == null) {
  72. toEmails = Collections.emptyList();
  73. } else if (toBccEmails == null) {
  74. toBccEmails = Collections.emptyList();
  75. }
  76. MimeMessage message;
  77. try {
  78. message = mailService.createMimeMessage();
  79. } catch (Exception e) {
  80. throw new EmailException(e);
  81. }
  82. MimeMessageHelper helper;
  83. try {
  84. helper = new MimeMessageHelper(message, attachments != null && !attachments.isEmpty(), AppConstants.CHARSET);
  85. helper.setValidateAddresses(true);
  86. helper.setFrom(fromEmail);
  87. } catch (Exception e) {
  88. throw new EmailException(e);
  89. }
  90. // To field
  91. addEmailRecipients(toEmails, toNames, helper, false);
  92. // Bcc field
  93. addEmailRecipients(toBccEmails, toBccNames, helper, true);
  94. subject = clean(subject);
  95. try {
  96. helper.setSubject(subject);
  97. helper.setText(content, isHtml);
  98. } catch (Exception e) {
  99. throw new EmailException(e);
  100. }
  101. if (attachments != null) {
  102. for (EmailAttachment attachment : attachments) {
  103. try {
  104. String contentType = MimeUtil.getContentType(attachment.getMimeType(), attachment.getEncoding());
  105. helper.addAttachment(attachment.getFileName(), attachment.getInputStreamSource(), contentType);
  106. } catch (AlfrescoRuntimeException e) {
  107. throw e;
  108. } catch (Exception e) {
  109. throw new EmailException(e);
  110. }
  111. }
  112. }
  113. if (log.isDebugEnabled()) {
  114. Address[] recipients;
  115. try {
  116. recipients = message.getAllRecipients();
  117. } catch (Exception e) {
  118. throw new EmailException(e);
  119. }
  120. log.debug("Sending out email: " + Arrays.toString(recipients));
  121. }
  122. try {
  123. long step1 = System.currentTimeMillis();
  124. mailService.send(message);
  125. long step2 = System.currentTimeMillis();
  126. // Write copy of the message after it has been send, then it has same header as were set during sending
  127. String info = "";
  128. if (StringUtils.isNotBlank(messageCopyFolder)) {
  129. try {
  130. String filename = "MimeMessage-" + dateFormat.format(new Date());
  131. File messageFile = new File(messageCopyFolder, filename);
  132. FileOutputStream messageOutputStream = new FileOutputStream(messageFile);
  133. try {
  134. message.writeTo(messageOutputStream, new String[] { "Bcc", "Content-Length" });
  135. } finally {
  136. IOUtils.closeQuietly(messageOutputStream);
  137. }
  138. info = "\n wrote message to file " + messageFile;
  139. } catch (Exception e) {
  140. log.error("Error copying message contents to file", e);
  141. }
  142. }
  143. MonitoringUtil.logSuccess(MonitoredService.OUT_SMTP);
  144. if (log.isInfoEnabled()) {
  145. log.info("sendEmail service call took " + (step2 - step0) + " ms\n prepare message - " + (step1 - step0) + " ms\n send message - "
  146. + (step2 - step1) + " ms" + info);
  147. }
  148. } catch (AlfrescoRuntimeException e) {
  149. MonitoringUtil.logError(MonitoredService.OUT_SMTP, e);
  150. throw e;
  151. } catch (Exception e) {
  152. MonitoringUtil.logError(MonitoredService.OUT_SMTP, e);
  153. throw new EmailException(e);
  154. }
  155. }
  156. private static String clean(String input) {
  157. // Forbids the use of characters in range 1-31 (i.e., 0x01-0x1F)
  158. return input == null ? null : input.replaceAll("\\p{Cntrl}", " ");
  159. }
  160. @Override
  161. public List<EmailAttachment> getAttachments(List<NodeRef> fileRefs, boolean zipIt, List<X509Certificate> encryptionCertificates, String zipAndEncryptFileTitle) throws Exception {
  162. List<EmailAttachment> attachments = new ArrayList<EmailAttachment>();
  163. if (fileRefs != null && !fileRefs.isEmpty()) {
  164. String containerExtension = null;
  165. // If both ZIP and CDOC is selected, then only produce CDOC and ignore ZIP, because CDOC also compresses files
  166. if (encryptionCertificates != null && !encryptionCertificates.isEmpty()) {
  167. containerExtension = "cdoc";
  168. } else if (zipIt) {
  169. containerExtension = "zip";
  170. }
  171. if (containerExtension != null) {
  172. String fileName = FilenameUtil.buildFileName(zipAndEncryptFileTitle, containerExtension);
  173. final File tmpFile = TempFileProvider.createTempFile("sendout-", "." + containerExtension);
  174. AlfrescoTransactionSupport.bindListener(new TransactionListenerAdapter() {
  175. @Override
  176. public void beforeCompletion() {
  177. tmpFile.delete();
  178. }
  179. });
  180. try {
  181. OutputStream tmpOutput = new BufferedOutputStream(new FileOutputStream(tmpFile));
  182. if (encryptionCertificates != null && !encryptionCertificates.isEmpty()) {
  183. if(BeanHelper.getDigisignCryptService().isActive()) {
  184. log.info("Using DigiSign-service to make CDOC container...");
  185. BeanHelper.getDigisignCryptSearches().makeCdoc(encryptionCertificates, fileRefs, tmpOutput);
  186. } else {
  187. log.info("Using DELTA to make CDOC container...");
  188. getSignatureService().writeEncryptedContainer(tmpOutput, fileRefs, encryptionCertificates, fileName);
  189. }
  190. } else {
  191. generalService.writeZipFileFromFiles(tmpOutput, fileRefs);
  192. }
  193. } catch (FileNotFoundException e) {
  194. throw new RuntimeException(e);
  195. }
  196. InputStreamSource contentSource = new FileSystemResource(tmpFile);
  197. String mimeType = mimetypeService.guessMimetype(fileName);
  198. attachments.add(new EmailAttachment(fileName, mimeType, AppConstants.CHARSET, contentSource, fileRefs.get(0)));
  199. } else {
  200. for (NodeRef fileRef : fileRefs) {
  201. FileInfo fileInfo = fileFolderService.getFileInfo(fileRef);
  202. ContentData contentData = fileInfo.getContentData();
  203. AlfrescoContentSource contentSource = new AlfrescoContentSource(fileRef);
  204. attachments.add(new EmailAttachment(fileInfo.getName(), contentData.getMimetype(), contentData.getEncoding(), contentSource, fileRef));
  205. }
  206. }
  207. }
  208. return attachments;
  209. }
  210. @Override
  211. public long getAttachmentsTotalSize(List<NodeRef> fileRefs, boolean zipIt, boolean encrypt) {
  212. long size = 0;
  213. // CDOC uses the same compression algorithm as ZIP (Deflater#DEFAULT_COMPRESSION)
  214. if (zipIt || encrypt) {
  215. CountingOutputStream tmpOutput = new CountingOutputStream(new NullOutputStream());
  216. if (encrypt) {
  217. DeflaterOutputStream zipOutput = new DeflaterOutputStream(tmpOutput);
  218. try {
  219. getDigiDoc4JSignatureService().writeContainer(zipOutput, fileRefs);
  220. } finally {
  221. IOUtils.closeQuietly(zipOutput);
  222. }
  223. size = tmpOutput.getCount() / 3 * 4; // CDOC encodes data in Base64
  224. size += size / 64; // CDOC adds one newline character every 64 characters
  225. size += 3072; // CDOC XML data (if one recipient) adds approx. 3 KB
  226. } else {
  227. generalService.writeZipFileFromFiles(tmpOutput, fileRefs);
  228. size = tmpOutput.getCount();
  229. }
  230. } else {
  231. for (NodeRef fileRef : fileRefs) {
  232. FileInfo fileInfo = fileFolderService.getFileInfo(fileRef);
  233. ContentData contentData = fileInfo.getContentData();
  234. size += contentData.getSize();
  235. }
  236. }
  237. return size;
  238. }
  239. // /// PRIVATE METHODS
  240. private MimeMessageHelper addEmailRecipients(List<String> toEmails, List<String> toNames, MimeMessageHelper helper, boolean asBcc) throws EmailException {
  241. String encoding = helper.getEncoding();
  242. for (int i = 0; i < toEmails.size(); i++) {
  243. String toEmail = toEmails.get(i);
  244. InternetAddress toAddr;
  245. try {
  246. toAddr = new InternetAddress(toEmail);
  247. } catch (Exception e) {
  248. throw new EmailException(e);
  249. }
  250. if (toNames != null && toNames.size() == toEmails.size()) {
  251. String name = clean(toNames.get(i));
  252. if (StringUtils.isNotBlank(encoding)) {
  253. try {
  254. toAddr.setPersonal(name, encoding);
  255. } catch (Exception e) {
  256. throw new EmailException(e);
  257. }
  258. } else {
  259. try {
  260. toAddr.setPersonal(name);
  261. } catch (Exception e) {
  262. throw new EmailException(e);
  263. }
  264. }
  265. }
  266. try {
  267. if (asBcc) {
  268. helper.addBcc(toAddr);
  269. } else {
  270. helper.addTo(toAddr);
  271. }
  272. } catch (Exception e) {
  273. throw new EmailException(e);
  274. }
  275. }
  276. return helper;
  277. }
  278. // /// GETTERS AND SETTERS
  279. public void setMailService(JavaMailSender mailService) {
  280. this.mailService = mailService;
  281. }
  282. public void setFileFolderService(FileFolderService fileFolderService) {
  283. this.fileFolderService = fileFolderService;
  284. }
  285. public void setGeneralService(GeneralService generalService) {
  286. this.generalService = generalService;
  287. }
  288. public void setMimetypeService(MimetypeService mimetypeService) {
  289. this.mimetypeService = mimetypeService;
  290. }
  291. public void setMessageCopyFolder(String messageCopyFolder) {
  292. this.messageCopyFolder = messageCopyFolder;
  293. }
  294. // /// CLASSES
  295. private SignatureService getSignatureService() {
  296. if (_signatureService == null) {
  297. _signatureService = BeanHelper.getSignatureService();
  298. }
  299. return _signatureService;
  300. }
  301. private DigiDoc4JSignatureService getDigiDoc4JSignatureService() {
  302. if (_digiDoc4JSignatureService == null) {
  303. _digiDoc4JSignatureService = BeanHelper.getDigiDoc4JSignatureService();
  304. }
  305. return _digiDoc4JSignatureService;
  306. }
  307. public class AlfrescoContentSource implements InputStreamSource {
  308. protected NodeRef nodeRef;
  309. public AlfrescoContentSource(NodeRef file) {
  310. nodeRef = file;
  311. }
  312. @Override
  313. @SuppressWarnings("synthetic-access")
  314. public InputStream getInputStream() throws IOException {
  315. return fileFolderService.getReader(nodeRef).getContentInputStream();
  316. }
  317. }
  318. }