Java tutorial
/* * * This is a simple Email Queue management system * Copyright (C) 2012 Imran M Yousuf (imyousuf@smartitengineering.com) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.smartitengineering.emailq.service.impl; import com.google.inject.Inject; import com.google.inject.Singleton; import com.google.inject.name.Named; import com.smartitengineering.cms.repo.dao.impl.ExtendedReadDao; import com.smartitengineering.dao.common.CommonDao; import com.smartitengineering.dao.common.queryparam.MatchMode; import com.smartitengineering.dao.common.queryparam.QueryParameter; import com.smartitengineering.dao.common.queryparam.QueryParameterFactory; import com.smartitengineering.emailq.domain.Email; import com.smartitengineering.emailq.domain.Email.Attachments; import com.smartitengineering.emailq.domain.Email.Message.MsgType; import com.smartitengineering.emailq.service.EmailService; import com.smartitengineering.emailq.service.Emails; import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.concurrent.Semaphore; import javax.activation.DataHandler; import javax.activation.DataSource; import javax.mail.Address; import javax.mail.Message; import javax.mail.Message.RecipientType; import javax.mail.MessagingException; import javax.mail.Multipart; import javax.mail.Session; import javax.mail.Transport; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeBodyPart; import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeMultipart; import javax.mail.util.ByteArrayDataSource; import org.apache.commons.lang.StringUtils; import org.quartz.DateIntervalTrigger; import org.quartz.Job; import org.quartz.JobDetail; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.Trigger; import org.quartz.impl.StdSchedulerFactory; import org.quartz.spi.JobFactory; import org.quartz.spi.TriggerFiredBundle; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * * @author imyousuf */ @Singleton public class EmailServiceImpl implements EmailService { @Inject private CommonDao<Email, String> commonDao; @Inject private ExtendedReadDao<Email, String> extendedReadDao; @Inject private Session session; @Inject(optional = true) @Named("mailSenderCronDelayInSeonds") private Integer period = new Integer(120); @Inject(optional = true) @Named("mailSenderCronEnabled") private Boolean cronEnabled = false; private Scheduler scheduler; private final Semaphore sendEmailMutex = new Semaphore(1); private final transient Logger logger = LoggerFactory.getLogger(getClass()); private Transport transport; @Inject public void initSendMailCron() { if (!cronEnabled.booleanValue()) { return; } try { transport = session.getTransport("smtp"); } catch (Exception ex) { logger.error("Could not initialize SMTP transport", ex); throw new IllegalStateException(ex); } try { scheduler = StdSchedulerFactory.getDefaultScheduler(); JobDetail detail = new JobDetail("sendEmailJob", "sendEmailPoll", SendEmailJob.class); Trigger trigger = new DateIntervalTrigger("sendEmailTrigger", "sendEmailPoll", DateIntervalTrigger.IntervalUnit.SECOND, period.intValue()); scheduler.setJobFactory(new JobFactory() { public Job newJob(TriggerFiredBundle bundle) throws SchedulerException { try { Class<? extends Job> jobClass = bundle.getJobDetail().getJobClass(); if (EmailServiceImpl.class.equals(jobClass.getEnclosingClass())) { Constructor<? extends Job> constructor = (Constructor<? extends Job>) jobClass .getDeclaredConstructors()[0]; constructor.setAccessible(true); Job job = constructor.newInstance(EmailServiceImpl.this); return job; } else { return jobClass.newInstance(); } } catch (Exception ex) { throw new SchedulerException(ex); } } }); scheduler.start(); scheduler.scheduleJob(detail, trigger); } catch (Exception ex) { logger.error("Could not start cron job!", ex); throw new IllegalStateException(ex); } } private class SendEmailJob implements Job { public void execute(JobExecutionContext context) throws JobExecutionException { try { sendEmailMutex.acquire(); } catch (Exception ex) { logger.warn("Could not acquire lock!", ex); throw new JobExecutionException(ex); } try { sendPendingEmails(); } catch (Exception ex) { logger.error("Error sending pending emails", ex); throw new JobExecutionException(ex); } finally { sendEmailMutex.release(); } } } protected void sendPendingEmails() throws Exception { QueryParameter statusParam = QueryParameterFactory.getStringLikePropertyParam(Email.PROPERTY_MAILSTATUS, Email.MailStatus.NOT_SENT.name(), MatchMode.EXACT); long count = extendedReadDao.count(statusParam); if (count > 0) { Collection<Email> emails = commonDao.getList(statusParam, QueryParameterFactory.getMaxResultsParam((int) count), QueryParameterFactory.getFirstResultParam(0)); if (emails != null && !emails.isEmpty()) { if (logger.isInfoEnabled()) { logger.info(new StringBuilder("Number of messages attempting to send ").append(emails.size()) .toString()); } List<Email> successfulEmails = new ArrayList<Email>(); logger.debug("Connecting to SMTP server"); transport.connect(); try { for (Email email : emails) { sendEmail(email, successfulEmails); } } finally { logger.debug("Closing tunnel with SMTP server"); transport.close(); } if (!successfulEmails.isEmpty()) { if (logger.isInfoEnabled()) { logger.info(new StringBuilder("Number of messages sent ").append(successfulEmails.size()) .toString()); } commonDao.update(successfulEmails.toArray(new Email[successfulEmails.size()])); } } } } protected void sendEmail(Email email, List<Email> successfulEmails) { try { if (logger.isDebugEnabled()) { logger.debug("Attempting to send " + email.getId() + " " + email.getSubject()); if (email.getTo() != null) { logger.debug("To: " + Arrays.toString(email.getTo().toArray())); } else { logger.debug("To is NULL"); } if (email.getTo() != null) { logger.debug("CC: " + Arrays.toString(email.getCc().toArray())); } else { logger.debug("CC is NULL"); } if (email.getTo() != null) { logger.debug("BCC: " + Arrays.toString(email.getBcc().toArray())); } else { logger.debug("BCC is NULL"); } if (email.getTo() != null) { logger.debug("FROM: " + email.getFrom()); } else { logger.debug("FROM is NULL"); } if (email.getAttachments() != null) { logger.debug("Attachments: " + Arrays.toString(email.getAttachments().toArray())); for (Attachments attachment : email.getAttachments()) { logger.debug("Attachment: " + attachment.getName()); } } else { logger.debug("No attachments"); } } //Send email if (StringUtils.isBlank(email.getSubject()) || StringUtils.isBlank(email.getFrom())) { logger.warn(new StringBuilder("Invalid email without either from or a subject, thus ignoring it ") .append(email.getId()).toString()); return; } MimeMessage message = new MimeMessage(session); message.setSubject(email.getSubject()); message.setFrom(new InternetAddress(email.getFrom())); addRecipients(message, Message.RecipientType.TO, email.getTo()); addRecipients(message, Message.RecipientType.CC, email.getCc()); addRecipients(message, Message.RecipientType.BCC, email.getBcc()); if (email.getMessage() != null && StringUtils.isNotBlank(email.getMessage().getMsgBody()) && email.getMessage().getMsgType().equals(MsgType.PLAIN) && (email.getAttachments() == null || email.getAttachments().isEmpty())) { message.setText(email.getMessage().getMsgBody()); } else { Multipart multipart = new MimeMultipart(); if (email.getMessage() != null && StringUtils.isNotBlank(email.getMessage().getMsgBody())) { MimeBodyPart bodyPart = new MimeBodyPart(); switch (email.getMessage().getMsgType()) { case HTML: bodyPart.setContent(email.getMessage().getMsgBody(), "html"); break; case PLAIN: default: bodyPart.setText(email.getMessage().getMsgBody()); } multipart.addBodyPart(bodyPart); } if (email.getAttachments() != null && !email.getAttachments().isEmpty()) { for (Attachments attachment : email.getAttachments()) { addAttachment(multipart, attachment); } } message.setContent(multipart); } Transport.send(message); if (logger.isDebugEnabled()) { logger.debug("Sent " + email.getId()); } //Update status email.setMailStatus(Email.MailStatus.SENT); successfulEmails.add(email); if (logger.isDebugEnabled()) { logger.debug("Set new mail status and add to successful queue " + email.getSubject()); } } catch (Exception ex) { logger.warn( new StringBuilder("Error sending email with subject ").append(email.getSubject()).toString(), ex); } } protected void addRecipients(MimeMessage message, RecipientType recipientType, Collection<String> addresses) throws MessagingException { if (addresses != null && !addresses.isEmpty()) { Address[] addressArray = new Address[addresses.size()]; int index = 0; for (String address : addresses) { addressArray[index++] = new InternetAddress(address); } message.addRecipients(recipientType, addressArray); } } private void addAttachment(Multipart multipart, Attachments attachment) throws MessagingException { MimeBodyPart attachmentPart = new MimeBodyPart(); attachmentPart.setFileName(attachment.getName()); if (StringUtils.isNotBlank(attachment.getDescription())) { attachmentPart.setDescription(attachment.getDescription()); } if (StringUtils.isNotBlank(attachment.getDisposition())) { attachmentPart.setDisposition(attachment.getDisposition()); } DataSource source = new ByteArrayDataSource(attachment.getBlob(), attachment.getContentType()); attachmentPart.setDataHandler(new DataHandler(source)); multipart.addBodyPart(attachmentPart); } public boolean saveEmail(com.smartitengineering.emailq.domain.Email email) { // No deliverable configured if ((email.getTo() == null || email.getTo().isEmpty()) && (email.getCc() == null || email.getCc().isEmpty()) && (email.getBcc() == null || email.getBcc().isEmpty())) { logger.warn("Ignoring email as no deliverable address is set!"); return false; } // No from configured if (StringUtils.isBlank(email.getFrom())) { logger.warn("No From configured!"); return false; } // No message body if (email.getMessage() == null || email.getMessage().getMsgType() == null || StringUtils.isBlank(email.getMessage().getMsgBody())) { logger.warn("No message body set!"); return false; } try { email.setMailStatus(Email.MailStatus.NOT_SENT); commonDao.save(email); return true; } catch (Exception ex) { logger.warn("Could not save email", ex); return false; } } public Emails getEmails(com.smartitengineering.dao.common.queryparam.QueryParameter... params) { long count = extendedReadDao.count(params); Collection<Email> emailCollcn = commonDao.getList(params); Emails emails = new Emails(); emails.setTotalCount(count); emails.setEmails(emailCollcn); return emails; } }