这段时间通过kettle完成了一个产品到期提醒的功能,也就是通过发送会议邀约的方式实现定时提醒。由于kettle自带有发送邮件的组件,但并没有发送会议邀约的组件,所以只能通过Java代码组件来进行自行开发实现。而在实现这个的过程中,我踩了一些小坑,在此做个小总结,便于后期回顾,也期待能够借此帮助到他人。
首先介绍下分享流程:1. 了解使用java代码发送会议邀约的流程;2. 介绍通过kettle的java代码组件发送会议邀约的方式。说明:本次分享主要是针对outlook邮箱和foxmail邮箱,其他邮箱我并没有测试,想必效果应该也都是差不多的。
1.java代码发送会议邀约
代码很详细,注释也很清楚,下面直接上代码。
pom.xml依赖:
<dependency> <groupId>javax.mail</groupId> <artifactId>mail</artifactId> <version>1.4.7</version> </dependency>
code代码:
import javax.activation.DataHandler; import javax.activation.FileDataSource; import javax.mail.*; import javax.mail.internet.*; import javax.mail.util.ByteArrayDataSource; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.*; public class Email { public void send(String fromEmail, String toEmail, String emailPort, String emailHost, String valarmDt, String subject, String location, String emailContent) { try { Properties props = new Properties(); props.put("mail.smtp.port", emailPort); props.put("mail.smtp.host", emailHost); props.put("mail.transport.protocol", "smtp"); props.put("mail.smtp.auth", "true"); props.put("mail.smtp.starttls.enable", "true"); props.put("mail.smtp.ssl", "true"); Authenticator authenticator = new Authenticator() { @Override protected PasswordAuthentication getPasswordAuthentication() { String userId = "faJianRen@YouXiang.DiZhi"; String password = "PassWord"; return new PasswordAuthentication(userId, password); } }; Session session = Session.getInstance(props, authenticator); MimeMessage message = new MimeMessage(session); message.setFrom(new InternetAddress(fromEmail)); /* 设置多收件人,Message.RecipientType.CC:密送 Message.RecipientType.BCC:抄送 Message.RecipientType.TO:收件人 */ List<String> emails = new ArrayList<>(); String[] emailArrays = toEmail.split(" "); int a = emailArrays.length; for (String email : emailArrays) { emails.add(email); } message.addRecipients(Message.RecipientType.TO, setRecipientT0(emails)); message.setSubject(subject); StringBuffer buffer = new StringBuffer(); buffer.append("BEGIN:VCALENDAR\n" + "PRODID:-//Microsoft Corporation//Outlook 9.0 MIMEDIR//EN\n" + "VERSION:2.0\n" + "METHOD:REQUEST\n" //METHOD:CANCEL 取消会议 METHOD:REQUEST 创建和更新会议 + "BEGIN:VEVENT\n" + "ATTENDEE;ROLE=REQ-PARTICIPANT;RSVP=TRUE:MAILTO:" + toEmail + "\n" + "ORGANIZER:MAILTO:" + toEmail + "\n" + "DTSTART:" + getUtc(valarmDt + " 08:00") + "\n" + "DTEND:" + getUtc(valarmDt + " 19:00") + "\n" + "LOCATION:" + location + "\n" + "UID:" + UUID.randomUUID().toString() + "\n"//如果id相同的话,outlook会认为是同一个会议请求,所以使用uuid。 + "CATEGORIES:待办提醒\n" + "DESCRIPTION:" + emailContent + "\n\n" // 会议内容换行为\\n + "SUMMARY:汇总 \n" + "PRIORITY:5\n" + "CLASS:PUBLIC\n" + "BEGIN:VALARM\n" + "TRIGGER:-PT15M\n" + "ACTION:DISPLAY\n" + "DESCRIPTION:Reminder\n" + "END:VALARM\n" + "END:VEVENT\n" + "END:VCALENDAR"); BodyPart messageBodyPart = new MimeBodyPart(); // 测试下来如果不这么转换的话,会以纯文本的形式发送过去, //如果没有method=REQUEST;charset=\"UTF-8\",outlook会议附件的形式存在,而不是直接打开就是一个会议请求 messageBodyPart.setDataHandler(new DataHandler(new ByteArrayDataSource(buffer.toString(), "text/calendar;method=REQUEST;charset=\"UTF-8\""))); Multipart multipart = new MimeMultipart(); multipart.addBodyPart(messageBodyPart); // 添加附件 String[] paths = { "D:\\log\\2020-6-30.log", "D:\\log\\2020-7-1.log" }; for (String filePath : paths) { MimeBodyPart part = new MimeBodyPart(); FileDataSource fds = new FileDataSource(filePath); part.setFileName(MimeUtility.encodeWord(fds.getName()));// MimeUtility.encodeWord文件名解决中文乱码 part.setDataHandler(new DataHandler(fds)); multipart.addBodyPart(part); } message.setContent(multipart); Transport.send(message); } catch (Exception ex) { ex.printStackTrace(); } } /*** 设置收件人/抄送人/密送人地址信息*/ private InternetAddress[] setRecipientT0(List<String> recipientT0List) throws Exception { if (recipientT0List.size() > 0) { InternetAddress[] sendTo = new InternetAddress[recipientT0List.size()]; for (int i = 0; i < recipientT0List.size(); i++) { System.out.println("发送到:" + recipientT0List.get(i)); sendTo[i] = new InternetAddress(recipientT0List.get(i), "", "UTF-8"); } return sendTo; } return null; } /** * 转utc时间 */ private static String getUtc(String str) throws Exception { SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd HH:mm"); long millionSeconds = 0; millionSeconds = sdf.parse(str).getTime(); //utc时间差8小时 long currentTime = millionSeconds - 8 * 60 * 60 * 1000; Date date = new Date(currentTime); DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String nowTime = df.format(date); String utcTime = nowTime.replace("-", "").replace(" ", "T").replace(":", ""); return utcTime; } public static void main(String[] args) { try { Email email = new Email(); String fromEmail = "faJianRen@YouXiang.DiZhi"; String toEmail = "XIAGUANGHUI@houzhui.com"; String emailPort = "25"; String emailHost = "10.8.88.88"; String valarmDt = "20200910"; String subject = "subject产品到期提醒20200909"; String location = "1号会议室(此处完全可以写成自定义的内容,不必拘泥于会议邀约)"; String emailContent = "2020-0001|产品1|2020-08-30|200000.00|固定分配|李飞\\n" + "2020-0002|产品2|2020-08-30|200000.00|固定分配2|李飞2"; email.send(fromEmail, toEmail, emailPort, emailHost, valarmDt, subject, location, emailContent); System.out.println("success"); System.out.println(System.currentTimeMillis()); } catch (Exception e) { e.printStackTrace(); } } }
效果展示(outlook邮箱):
网络异常,图片无法展示
|
outlook会议邀约效果展示
2.使用kettle java代码组件发送会议邀约
说明:我的kettle版本8.2,在开发前,需copy到依赖的jar包到kettle\data-integration\lib目录下,依赖jar包分别为javaws.jar、javax.mail.jar、rt.jar。
网络异常,图片无法展示
|
kettle
java代码:
// 导入依赖jar import javax.activation.DataHandler; import javax.activation.FileDataSource; import javax.mail.*; import javax.mail.internet.*; import javax.mail.util.ByteArrayDataSource; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.*; public boolean processRow(StepMetaInterface smi, StepDataInterface sdi) throws Exception { Object[] r = getRow(); if (r == null) { setOutputDone(); return false; } String haha; if (first) { first = false; } Object[] outputRow = createOutputRow(r, data.outputRowMeta.size()); String fromEmail = "faJianRen@YouXiang.DiZhi"; String toEmail = getParameter("toEmail"); String valarmDt = getParameter("valarmDt"); String emailContent = null; String dqCount = getParameter("DQCOUNT"); int count = Integer.parseInt(dqCount); if(count > 20){ emailContent = valarmDt+"存在" + dqCount + "个产品项目到期,具体详情请查看附件!"; }else{ emailContent = "产品到期列表如下:\\n" + get(Fields.In, "content_dqtx").getString(r); } String result = send(fromEmail, toEmail, valarmDt, emailContent); //get(Fields.Out, "RESULTMSG").setValue(outputRow, result); //输出参数 putRow(data.outputRowMeta, outputRow); return true; } private class EmailAuthenticator extends Authenticator { protected PasswordAuthentication getPasswordAuthentication() { String userId = "faJianRen@YouXiang.DiZhi"; String password = "PASSWORD"; return new PasswordAuthentication(userId, password); } } public String send(String fromEmail, String toEmail, String valarmDt, String emailContent) { try { Properties props = new Properties(); try { props.put("mail.smtp.port", "25"); props.put("mail.smtp.host", "10.8.88.88"); props.put("mail.transport.protocol", "smtp"); props.put("mail.smtp.auth", "true"); props.put("mail.smtp.starttls.enable", "true"); props.put("mail.smtp.ssl", "true"); } catch (Exception e) { e.printStackTrace(); return "-1"; } Session session; Authenticator authenticator = new EmailAuthenticator(); session = Session.getInstance(props, authenticator); MimeMessage message = new MimeMessage(session); message.setFrom(new InternetAddress(fromEmail)); //message.addRecipient(Message.RecipientType.TO, new InternetAddress(toEmail)); List emails = new ArrayList(); String[] emailArrays = toEmail.split(" "); for (int i = 0; i < emailArrays.length; i++) { emails.add(emailArrays[i]); } message.addRecipients(Message.RecipientType.TO,setRecipientT0(emails)); message.setSubject("产品到期提醒"); StringBuffer buffer = new StringBuffer(); buffer.append("BEGIN:VCALENDAR\n" + "PRODID:-//Microsoft Corporation//Outlook 9.0 MIMEDIR//EN\n" + "VERSION:2.0\n" + "METHOD:REQUEST\n" + "BEGIN:VEVENT\n" + "ATTENDEE;ROLE=REQ-PARTICIPANT;RSVP=TRUE:MAILTO:" + toEmail + "\n" + "ORGANIZER:MAILTO:" + toEmail + "\n" + "DTSTART:" + getUtc(valarmDt + " 09:00") + "\n" + "DTEND:" + getUtc(valarmDt + " 09:30") + "\n" + "LOCATION:到期提醒\n" + "UID:" + UUID.randomUUID().toString() + "\n"//如果id相同的话,outlook会认为是同一个会议请求,所以使用uuid。 + "CATEGORIES:待办提醒\n" + "DESCRIPTION:" + emailContent + "\n\n" + "SUMMARY:产品到期提醒\n" + "PRIORITY:5\n" + "CLASS:PUBLIC\n" + "BEGIN:VALARM\n" + "TRIGGER:-PT15M\n" + "ACTION:DISPLAY\n" + "DESCRIPTION:Reminder\n" + "END:VALARM\n" + "END:VEVENT\n" + "END:VCALENDAR"); BodyPart messageBodyPart = new MimeBodyPart(); // 测试下来如果不这么转换的话,会以纯文本的形式发送过去, //如果没有method=REQUEST;charset=\"UTF-8\",outlook会议附件的形式存在,而不是直接打开就是一个会议请求 messageBodyPart.setDataHandler(new DataHandler(new ByteArrayDataSource(buffer.toString(), "text/calendar;method=REQUEST;charset=\"UTF-8\""))); Multipart multipart = new MimeMultipart(); multipart.addBodyPart(messageBodyPart); MimeBodyPart part = new MimeBodyPart(); FileDataSource fds = new FileDataSource("D:\\upload\\kettle_job\\\product_dqfp_rili_tixing\\产品到期项目列表.xls"); part.setFileName(MimeUtility.encodeWord(fds.getName()));// MimeUtility.encodeWord文件名解决中文乱码 part.setDataHandler(new DataHandler(fds)); multipart.addBodyPart(part); message.setContent(multipart); Transport.send(message); } catch (MessagingException me) { me.printStackTrace(); return me.getMessage(); } catch (Exception ex) { ex.printStackTrace(); return "-3"; } return "Success"; } private InternetAddress[] setRecipientT0(List recipientT0List) throws Exception { if (recipientT0List.size() > 0) { InternetAddress[] sendTo = new InternetAddress[recipientT0List.size()]; for (int i = 0; i < recipientT0List.size(); i++) { System.out.println("发送到:" + recipientT0List.get(i)); sendTo[i] = new InternetAddress(recipientT0List.get(i).toString(), "", "UTF-8"); } return sendTo; } return null; } /** * 转utc时间 */ private static String getUtc(String str) { SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd HH:mm"); long millionSeconds = 0; try { millionSeconds = sdf.parse(str).getTime(); } catch (ParseException e1) { e1.printStackTrace(); } //utc时间差8小时 long currentTime = millionSeconds - 8 * 60 * 60 * 1000; Date date = new Date(currentTime); DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String nowTime = df.format(date); String utcTime = nowTime.replace("-", "").replace(" ", "T").replace(":", ""); return utcTime; }
在使用kettle的java代码组件时,发现如下小坑,在此总结说明一下,以防后面开发重复踩坑:
(1) 不支持@override注解,故以上代码EmailAuthenticator,没有使用内部类的方式进行初始化;
(2) 无法识别泛型的尖括号<>,可以观察到,原本应该使用泛型的地方,都删除掉了泛型。
3.参考资料
https://blog.csdn.net/han949417140/article/details/90206475