Skip to content

Instantly share code, notes, and snippets.

@an-sangkil
Created October 4, 2016 02:49
Show Gist options
  • Save an-sangkil/a8927b26020a1354f5ad324b11e29402 to your computer and use it in GitHub Desktop.
Save an-sangkil/a8927b26020a1354f5ad324b11e29402 to your computer and use it in GitHub Desktop.
Bulk Main Sender
package com.knkcorp.tms.common.mail;
import java.util.List;
import javax.mail.Message;
import org.springframework.stereotype.Component;
@Component
public interface BaseMailSender {
/**
* 메일 발송
* @param from
* @param to
* @param subject
* @param text
*/
public void send(String from, String to, String subject, String text);
public void send(String from, String[] to , String subject , String textHTML);
public void send(String from, String[] to , String[] cc , String[] bcc ,String subject , String textHTML);
void send(List<Message> messages);
}
package com.knkcorp.tms.web.service;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import javax.mail.Message;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import com.knkcorp.tms.common.mail.BaseMailSender;
/**
* Description : 이메일을 병렬로 처리 하기 위한 ThreadExceution 서비스
* BaseMailSender.java는 기본적으로 @primary 로 지정된 SmtpJavaMailService를 사용한다.
*
* BaseMailSender를 직접 인스턴스를 사용할경우.
* SimpleMailSender를 생성자에 주입하여 사용 할 수 있다.
*
* @author skan
* @since 2016. 9. 29.
* @version
*
*/
@Service
public class ExecutorSendMailService {
private Logger logger = LoggerFactory.getLogger(getClass());
@Inject
private BaseMailSender mailSend;
public ExecutorSendMailService(BaseMailSender mailSend){
if(executorService == null || executorService.isTerminated()) {
int codeNumber = Runtime.getRuntime().availableProcessors();
this.executorService = new ThreadPoolExecutor(codeNumber, codeNumber, 100L, TimeUnit.MILLISECONDS, blockingQueue);
logger.debug("new threadakeExecuter {} " , executorService);
} else {
logger.debug("old threadakeExecuter {} " , executorService);
}
this.mailSend = mailSend;
}
// 이메일 보낼 Thread 구분지어질 방법
public enum ThreadSendEnum {
CALLABLE,
RUNNABLE
}
// executor Service 생성
// static ExecutorService executorService = Executors.newFixedThreadPool(4);
BlockingQueue<Runnable> blockingQueue = new LinkedBlockingQueue<Runnable>();
private ExecutorService executorService;
public void sendEmail(String from, String to, String subject, String text, ThreadSendEnum sendType) {
logger.trace("최대 코어 스레드수 = {}" ,Runtime.getRuntime().availableProcessors());
try {
switch (sendType) {
// Return 이 필요한 경우이며, 결과반환을 위해 완료대기 까지 대기시간이 있다.
case CALLABLE:
Future<Integer> sendCountFuture = executorService.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executorService;
int poolSize = threadPoolExecutor.getPoolSize();
String threadName = Thread.currentThread().getName();
logger.info("[총 스레드 개수: " + poolSize + "] 작업 스레드 이름: " + threadName);
int successCount = 0;
mailSend.send(from, to, subject, text);
return successCount+1;
}
});
try {
sendCountFuture.get();
} catch (InterruptedException | ExecutionException e1) {
e1.printStackTrace();
}
break;
// 결과값 없음
case RUNNABLE :
executorService.submit(new Runnable() {
@Override
public void run() {
ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executorService;
int poolSize = threadPoolExecutor.getPoolSize();
String threadName = Thread.currentThread().getName();
logger.info("[총 스레드 개수: " + poolSize + "] 작업 스레드 이름: " + threadName);
mailSend.send(from, to, subject, text);
}
});
break;
}
} catch (Exception e) {
logger.debug("이메일 발송 에러 = {}" , e);
} finally {
//웹환경에서는 사용하지 않음.
//executorService.shutdown();
}
}
/**
* 대량 메일 발송.
* @param messages
* @param sendType
*/
public void bulkSendMail(List<Message> messages, ThreadSendEnum sendType){
try {
switch (sendType) {
case CALLABLE :
Future<Integer> successCount = executorService.submit(new Callable<Integer>() {
@Override
public Integer call() {
int successCount = 0;
ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executorService;
int poolSize = threadPoolExecutor.getPoolSize();
String threadName = Thread.currentThread().getName();
logger.info("[총 스레드 개수: " + poolSize + "] 작업 스레드 이름: " + threadName);
mailSend.send(messages);
return successCount+1;
}
});
logger.info("발송 {} 건 중 {} 건이 발송 되었습니다.", messages.size(), successCount);
break;
case RUNNABLE :
executorService.submit(new Runnable() {
@Override
public void run() {
ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executorService;
int poolSize = threadPoolExecutor.getPoolSize();
String threadName = Thread.currentThread().getName();
logger.info("[총 스레드 개수: " + poolSize + "] 작업 스레드 이름: " + threadName);
mailSend.send(messages);
}
});
break;
}
} catch (Exception e) {
logger.debug("이메일 발송 에러 = {}" , e);
} finally {
//웹환경에서는 사용하지 않음.
//executorService.shutdown();
}
}
// Thread 종료
public void emailSendShotdown() {
executorService.shutdown();
// 모두 종료되었는지 종료대기까지 기다린다. waiting~!
while (!executorService.isTerminated()) {
logger.info("THREAD_POOLE_XECUTOR shutdown and isWait......1");
new Thread(new Runnable() {
@Override
public void run() {
logger.info("THREAD_POOLE_XECUTOR shutdown and isWait......2");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
}
package com.knkcorp.tms.common.mail;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.PostConstruct;
import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.internet.MimeMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Primary;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;
/**
* DB 환경설정의 Default 파일을 읽어와서 메일 발송
* 프로퍼티 값으로 설정된 내역을 사용하지 않고, DB에 값을 사용하기 때문에 동적인 메일 셋팅이 가능한 클래스이다.
*
* @author skan
* @since 2016. 8. 22.
* @version
*
*/
@Primary
@Component
public class SmtpJavaMailSender implements BaseMailSender{
private Logger logger = LoggerFactory.getLogger(getClass());
private String host;
private String protocol;
private int port;
private String username;
private String password;
private Session session;
public Session getSession() {
return session;
}
/**
* 전처리 : DB에서 추출한 mail 기본정보
* 프로퍼티 값으로 설정된 내역을 사용하지 않고, DB에 값을 사용하기 때문에 동적인 메일 셋팅이 가능한 클래스이다.
*/
@PostConstruct
public void mailConfigration() {
// EMail 정보에 대한 환경설정 값을 DB로 부터 읽어온다.
ConcurrentHashMap<String, String> mailConfigration = null;
if(mailConfigration == null || mailConfigration.isEmpty()) {
this.host = "smtp.worksmobile.com";
this.protocol = "smtp";
this.port = 465;
this.username = "";
// 암/복호화 필요.
this.password = "";
}
Properties props = new Properties();
props.setProperty("mail.smtp.host", host);
props.setProperty("mail.smtp.port", port + "");
props.setProperty("mail.host", host);
props.setProperty("mail.transport.protocol", protocol);
//props.setProperty("mail.smtp.localhost", "localhost");
props.put("mail.smtp.ssl.enable", "true");
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtps.ssl.checkserveridentity", "true");
props.put("mail.smtps.ssl.trust", "*");
Authenticator auth = null;
if (username != null && password != null) {
auth = new Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
};
}
session = Session.getInstance(props, auth);
}
@Override
public void send(String from, String to, String subject, String textHTML) {
this.send(from, new String[]{to}, subject, textHTML);
}
@Override
public void send(String from, String[] to, String subject , String textHTML ) {
this.send(from, to, null, null, subject, textHTML);
}
@Override
public void send(String from, String[] to, String[] cc, String[] bcc, String subject, String textHTML) {
JavaMailSenderImpl sender = new JavaMailSenderImpl();
sender.setDefaultEncoding("utf-8");
sender.setSession(session);
try {
MimeMessage message = sender.createMimeMessage();
// use the true flag to indicate you need a multipart message
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setFrom(from);
helper.setTo(to);
// use the true flag to indicate the text included is HTML
helper.setSubject(subject);
helper.setText(textHTML, true);
// let's include the infamous windows Sample file (this time copied to c:/)
//FileSystemResource res = new FileSystemResource(new File("c:/Sample.jpg"));
sender.send(message);
logger.debug("Send Mail Success... = {}" , to.toString());
} catch (Exception e) {
logger.error("{} 님메일 발송에 실패하였습니다. 에러메세지 = {} " , to.toString() , e.getMessage());
// TODO : 메일 발송에 실패한 사용자 내역 관리.
}
}
/**
* 대량 메일 발송
* @param messages
*/
@Override
public void send(List<Message> messages) {
try {
JavaMailSenderImpl sender = new JavaMailSenderImpl();
sender.setDefaultEncoding("utf-8");
sender.setSession(session);
MimeMessage[] mailMessageArr = new MimeMessage[messages.size()];
messages.toArray(mailMessageArr);
sender.send(mailMessageArr);
logger.debug("Send Mail Success... = {}" , mailMessageArr.toString());
} catch (Exception e) {
logger.error("BULK 메일 발송에 실패하였습니다. 에러메세지 = {} " , e.getMessage());
// TODO : 메일 발송에 실패한 사용자 내역 관리.
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment