Provided in this part is a utility class (as promised) that converts a java mail mime message instance into a portable message bean, or vise verse. A simple static method (mimeToBean) is provided that takes a java mail mime message as input, and returns a portable message bean. Another static method (beanToMime) is provided to return a java mail mime message from a portable message bean.
In order to use this class, a string manipulation utility class is needed, which will be provided in Part 5.
/* * blog/javaclue/javamail/MessageBeanUtil.java * * Copyright (C) 2009 JackW
* * This program is free software: you can redistribute it and/or modify it under the terms of the * GNU Lesser General Public License as published by the Free Software Foundation, either version 3 * of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with this library. * If not, see <http://www.gnu.org/licenses/>. */ package blog.javaclue.javamail; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.net.UnknownHostException; import java.text.ParseException; import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; import java.util.Date; import java.util.Enumeration; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.activation.DataHandler; import javax.mail.Address; import javax.mail.BodyPart; import javax.mail.Header; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.Multipart; import javax.mail.Part; import javax.mail.Session; import javax.mail.Message.RecipientType; import javax.mail.internet.AddressException; import javax.mail.internet.InternetAddress; import javax.mail.internet.MailDateFormat; import javax.mail.internet.MimeBodyPart; import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeMultipart; import javax.mail.util.ByteArrayDataSource; import org.apache.log4j.Logger; public final class MessageBeanUtil { static final Logger logger = Logger.getLogger(MessageBeanUtil.class); static final boolean isDebugEnabled = logger.isDebugEnabled(); final static String LF = System.getProperty("line.separator", "\n"); static boolean debugSession = false; private static String hostName = null; public static final String RETURN_PATH = "Return-Path"; public static final String XHEADER_PRIORITY = "X-Priority"; public static final String XHEADER_MAILER = "X-Mailer"; public static final String MESSAGE_TRUNCATED = "=== message truncated ==="; private MessageBeanUtil() { } /** * convert JavaMail MimeMessage to message bean * * @param p - * part * @throws MessagingException * @throws IOException */ public static MessageBean mimeToBean(Part p) throws IOException, MessagingException { // make sure it's a message if (!(p instanceof Message) && !(p instanceof MimeMessage)) { // not a known message type try { logger.error("mimeToBean() - Unknown Part: " + p.getContentType()); logger.error("mimeToBean() - ---------------------------"); } catch (Exception e) { logger.error("Exception caught", e); } throw new MessagingException("Part was not a MimeMessage as expected"); } if (hostName == null) { try { hostName = java.net.InetAddress.getLocalHost().getHostName(); } catch (UnknownHostException e) { logger.warn("mimeToBean() - UnknownHostException caught, default to localhost", e); hostName = "localhost"; } } MessageBean msgBean = new MessageBean(); msgBean.clearParameters(); processEnvelope((Message) p, msgBean); processAttachment((BodypartBean) msgBean, p, msgBean, 0); return msgBean; } /* * process message envelope and headers * * @param msg - * a MimeMessage instance * @param msgBean - * a MessageBean instance * @return - SMTP message id, or null if not found * @throws AddressException */ private static String processEnvelope(Message msg, MessageBean msgBean) throws AddressException { Address[] from = null, received_to = null, envelope_to = null, cc = null, bcc = null, replyto = null; String[] xmailer = null; String subject = null; String messageId= null; java.util.Date receivedTime = null; // Received Date try { receivedTime = msg.getReceivedDate(); if (receivedTime != null) { msgBean.setSentDate(receivedTime); } } catch (MessagingException e) { logger.error("MessagingException caught during getReceivedDate()", e); } // retrieve Message-Id, Return-Path and Received Time from headers try { Enumeration<?> enu = ((MimeMessage) msg).getAllHeaders(); while (enu.hasMoreElements()) { Header hdr = (Header) enu.nextElement(); String name = hdr.getName(); if (isDebugEnabled) logger.debug("processEnvelope() - header line: " + name + ": " + hdr.getValue()); if ("Message-ID".equalsIgnoreCase(name)) { messageId= hdr.getValue(); logger.info("processEnvelope() - >>>>>Message-ID retrieved: " + messageId); msgBean.setSmtpMessageId(messageId); } if (RETURN_PATH.equalsIgnoreCase(name)) { msgBean.setReturnPath(hdr.getValue()); } if ("Date".equals(name) && receivedTime == null) { receivedTime = getHeaderDate(hdr.getValue()); // SMTP Date } } } catch (Exception e) { logger.error("Exception caught from getAllHeaderLines()", e); } Calendar rightNow = Calendar.getInstance(); // If Received DateTime not found from envelope, use current time if (receivedTime == null) { msgBean.setSentDate(rightNow.getTime()); } // display Received Date Time if (isDebugEnabled) { logger.info("processEnvelope() - Email Received Time: " + (receivedTime != null ? receivedTime.toString() : "UNKNOWN") + ", SERVER-TIME: " + rightNow.getTime().toString()); } String[] received = null; try { received = ((MimeMessage) msg).getHeader("Received"); } catch (MessagingException e) { logger.error("MessagingException caught from getHeader(Received)", e); } // retrieve TO address from "Received" Headers. String real_to = ""; if (received != null) { // sanity check, should never be null // scan received headers for "for" address, starting from the bottom // (the oldest) and going up until an email address is found. int i; String tmp_to = null; for (i = received.length - 1; i >= 0; i--) { if (isDebugEnabled) { logger.debug("processEnvelope() - Received: " + received[i]); } if ((tmp_to = analyzeReceived(received[i])) != null) { real_to = tmp_to; logger.info("processEnvelope() - found \"for\" in Received Headers: " + real_to); break; // exit loop } } } Address[] addr; // get FROM from envelope or Return-Path try { String[] _froms = null; if ((addr = msg.getFrom()) != null && addr.length > 0) { String addrStr = checkAddr(addr[0].toString()); for (int j = 1; j < addr.length; j++) { addrStr += "," + checkAddr(addr[j].toString()); } from = InternetAddress.parse(addrStr); } else if ((_froms = msg.getHeader(RETURN_PATH)) != null && _froms.length > 0) { logger.warn("processEnvelope() - FROM is missing from envelope, use Return-Path."); String addrStr = checkAddr(_froms[0]); for (int j = 1; j < _froms.length; j++) { addrStr += "," + checkAddr(_froms[j]); } from = InternetAddress.parse(addrStr); } else { // FROM is empty from envelope and Return-Path } } catch (MessagingException e) { logger.error("MessagingException caught from getFrom()", e); } msgBean.setFrom(from); if (isDebugEnabled) logger.debug("processEnvelope() - Email From Address: " + msgBean.getFromAsString()); // get TO from Received Headers if (real_to != null && real_to.trim().length() > 0) { // found TO address from header try { received_to = InternetAddress.parse(real_to); } catch (javax.mail.internet.AddressException e) { logger.error("Warning!!! AddressException caught from parsing " + real_to, e); } } // get TO from envelope try { if ((addr = msg.getRecipients(RecipientType.TO)) != null && addr.length > 0) { String addrStr = checkAddr(addr[0].toString()); for (int j = 1; j < addr.length; j++) { addrStr += "," + checkAddr(addr[j].toString()); } envelope_to = InternetAddress.parse(addrStr); } else { // TO is empty from envelope } } catch (MessagingException e) { logger.error("MessagingException caught from getRecipients(TO)", e); } msgBean.setToEnvelope(envelope_to); // TO from "Delivered-To" header Address[] delivered_to = null; try { String[] dlvrTo = msg.getHeader("Delivered-To"); if (dlvrTo != null && dlvrTo.length > 0) { String addrStr = checkAddr(dlvrTo[0]); for (int j=1; j<dlvrTo.length; j++) { addrStr += "," + checkAddr(dlvrTo[j]); } logger.info("processEnvelope() - \"Delivered-To\" found from header: " + addrStr); delivered_to = InternetAddress.parse(addrStr); } } catch (MessagingException e) { logger.error("MessagingException caught from msg.getHeader(\"Delivered-To\")", e); } // TO: Received (non-VERP) > Delivered-To > Received (VERP) > Envelope if (received_to != null && received_to.length > 0) { String dest = received_to[0] == null ? null : received_to[0].toString(); if (!StringUtil.isEmpty(dest) && !StringUtil.isVERPAddress(dest)) { msgBean.setTo(received_to); } } if (msgBean.getTo() == null) { if (delivered_to != null && delivered_to.length > 0) { // The real mailbox address this email is delivered to. If the email // address is a VERP address, the original address is restored from // the VERP address and is assigned to "Delivered-To" header. msgBean.setTo(delivered_to); } else if (received_to != null && received_to.length > 0) { // Email address extracted from "Received" header is the real email // address. But when VERP is enabled, since the Email Id is embedded // in the VERP address, every email received will have its own VERP // address. This will cause a disaster to EmailAddr table since all // TO addresses are saved to that table. String dest = received_to[0] == null ? null : received_to[0].toString(); if (!StringUtil.isEmpty(dest) && StringUtil.isVERPAddress(dest)) { String verpDest = StringUtil.getDestAddrFromVERP(dest); try { Address[] destAddr = InternetAddress.parse(verpDest); msgBean.setTo(destAddr); } catch (AddressException e) { logger.error("AddressException from Received_To: " + dest); } } } if (msgBean.getTo() == null) { msgBean.setTo(envelope_to); } } logger.info("processEnvelope() - Email To from Delivered-To: " + StringUtil.addrToString(delivered_to, false) + ", from Received Header: " + StringUtil.addrToString(received_to, false) + ", from Envelope: " + StringUtil.addrToString(envelope_to, false)); // CC try { if ((addr = msg.getRecipients(RecipientType.CC)) != null && addr.length > 0) { cc = addr; msgBean.setCc(cc); if (isDebugEnabled) logger.debug("processEnvelope() - Email CC Address: " + msgBean.getCcAsString()); } } catch (MessagingException e) { logger.error("MessagingException caught during getRecipients(CC)", e); } // BCC try { if ((addr = msg.getRecipients(RecipientType.BCC)) != null && addr.length > 0) { bcc = addr; msgBean.setBcc(bcc); if (isDebugEnabled) logger.debug("processEnvelope() - Email BCC Address: " + msgBean.getBccAsString()); } } catch (MessagingException e) { logger.error("MessagingException caught during getRecipients(BCC)", e); } // REPLYTO try { if ((addr = msg.getReplyTo()) != null && addr.length > 0) { String addrStr = checkAddr(addr[0].toString()); for (int j = 1; j < addr.length; j++) { addrStr += "," + checkAddr(addr[j].toString()); } replyto = InternetAddress.parse(addrStr); msgBean.setReplyto(replyto); } } catch (MessagingException e) { logger.error("MessagingException caught during getReplyTo()", e); } // SUBJECT try { subject = msg.getSubject(); } catch (MessagingException e) { logger.error("MessagingException caught during getSubject()", e); } msgBean.setSubject(subject); if (isDebugEnabled) logger.debug("processEnvelope() - Email Subject: [" + subject + "]"); // X-MAILER try { String[] hdrs = msg.getHeader(XHEADER_MAILER); if (hdrs != null) { xmailer = hdrs; msgBean.setXmailer(xmailer); } } catch (MessagingException e) { logger.error("MessagingException caught during getHeader(X-Mailer)", e); } // X-Priority: 1 (High), 2 (Normal), 3 (Low) try { String[] priority = ((MimeMessage) msg).getHeader(XHEADER_PRIORITY); if (priority != null) { msgBean.setPriority(priority); } } catch (MessagingException e) { logger.error("MessagingException caught during getHeader(X-Priority)", e); } return messageId; } // end of processEnvelope /* * recursively build up an attachment tree structure from a MultiPart * message. * * @param aNode - * current BodypartBean * @param p - * JavaMail part * @param msgBean - * root message bean * @param level - * attachment level */ private static void processAttachment(BodypartBean aNode, Part p, MessageBean msgBean, int level) { String disp = null, desc = null, contentType = "text/plain"; String dispOrig = null, descOrig = null; String fileName = null; // initialize part size int partSize = 0; // get content type try { contentType = p.getContentType(); } catch (Exception e) { contentType = "text/plain"; // failed to get content type, use default logger.error("Exception caught during getContentType()", e); } // get disposition try { dispOrig = p.getDisposition(); /* * disposition may look like: * 1) inline * 2) attachment * 3) attachment|inline; filename=xxxxx * I believe JavaMail is taking care of this. However the code * stays here just for safety. */ if (dispOrig != null && dispOrig.indexOf(";") > 0) { disp = dispOrig.substring(0, dispOrig.indexOf(";")); } else { disp = dispOrig; } } catch (Exception e) { logger.error("Exception caught during getDisposition()", e); } // get description try { descOrig = p.getDescription(); // to make use of "desc" field by saving attachment file name on it. if (descOrig == null) { // if null, get attachment filename from content type field desc = getFileName(contentType); // JavaMail appends file name to content type field if one is // found from disposition field } else { desc = descOrig; } } catch (Exception e) { logger.error("Exception caught during getDescription()", e); } // get file name try { fileName = p.getFileName(); } catch (Exception e) { logger.error("Exception caught during getFileName()", e); } // get part size try { partSize = p.getSize(); } catch (Exception e) { logger.error("Exception caught during getSize()", e); } // display some key information if (isDebugEnabled) { logger.info("processAttachment() - getDisposition(): " + dispOrig); logger.info("processAttachment() - getDescription(): " + descOrig); logger.info("processAttachment() - getContentType(): " + contentType + ", level:" + level + ", size:" + partSize); } if (fileName != null && isDebugEnabled) { logger.debug("processAttachment() - getFileName() = " + fileName); } // build mime tree try { aNode.setDisposition(disp); aNode.setDescription(desc); aNode.setFileName(fileName); // update attachment count if (Part.ATTACHMENT.equalsIgnoreCase(disp) || (Part.INLINE.equalsIgnoreCase(disp) && desc != null) || getFileName(contentType) != null) { // update attachment count msgBean.updateAttachCount(1); } // set content type and header fields aNode.setContentType(contentType); aNode.setHeaders(p); aNode.setSize(partSize); /* * Using isMimeType to determine the content type. */ if (p.isMimeType("text/plain") || p.isMimeType("text/html")) { logger.info("processAttachment(): level " + level + ", text message: " + contentType); aNode.setValue((String) p.getContent()); msgBean.getComponentsSize().add(Integer.valueOf(aNode.getSize())); } else if (p.isMimeType("multipart/*")) { logger.info("processAttachment(): level " + level + ", Recursive Multipart: " + contentType); Multipart mp = (Multipart) p.getContent(); int count = mp.getCount(); for (int i = 0; i < count; i++) { Part p1 = mp.getBodyPart(i); // call itself to build up a child attachment tree if (p1 != null) { BodypartBean subNode = new BodypartBean(); processAttachment(subNode, p1, msgBean, level+1); aNode.put(subNode); } } } else if (p.isMimeType("message/rfc822")) { // nested message type logger.info("processAttachment(): level " + level + ", RFC822 Message: " + contentType); Part p1 = (Part) p.getContent(); if (p1 != null) { BodypartBean subNode = new BodypartBean(); processAttachment(subNode, p1, msgBean, level+1); aNode.put(subNode); } } else { /* * other mime type. could be application, image, audio, video, message, etc. */ Object o = p.getContent(); /* * unknown mine type section. check its java type. */ if (o instanceof String) { // text type of section logger.info("processAttachment(): level " + level + ", String Content " + contentType); aNode.setValue((String) o); if (aNode.getValue() != null) { msgBean.getComponentsSize().add(Integer.valueOf(((byte[]) aNode.getValue()).length)); } } else if (o instanceof InputStream) { // stream type of section logger.info("processAttachment(): level " + level + ", InputStream Content " + contentType); InputStream is = (InputStream) o; aNode.setValue((InputStream) is); if (aNode.getValue() != null) { msgBean.getComponentsSize().add(Integer.valueOf(((byte[]) aNode.getValue()).length)); } } else if (o != null) { // unknown Java type, write it out as a string anyway. logger.error("processAttachment(): level " + level + ", Unknown type: " + o.toString()); aNode.setValue((String) o.toString()); if (aNode.getValue() != null) { msgBean.getComponentsSize().add(Integer.valueOf(((byte[]) aNode.getValue()).length)); } } else { // no content logger.error("processAttachment(): level " + level + ", Content is null"); aNode.setValue((Object)null); } } } // end of the try block catch (IndexOutOfBoundsException e) { /* thrown from mp.getBodyPart(i), should never happen */ logger.error("processAttachment(): IndexOutOfBoundsException caught: " + contentType); logger.error("IndexOutOfBoundsException caught", e); aNode.setValue("001: IndexOutOfBoundsException caught during process."); BodypartBean subNode = new BodypartBean("text/plain"); aNode.put(subNode); setAnodeValue(subNode, p, "002: IndexOutOfBoundsException thrown from mp.getBodyPart(i)."); if (subNode.getValue() != null) { msgBean.getComponentsSize().add(Integer.valueOf(((byte[]) subNode.getValue()).length)); } subNode.setDisposition(aNode.getDisposition()); subNode.setDescription(aNode.getDescription()); } catch (MessagingException e) { /* * JavaMail failed to read the message body, use its raw data instead */ logger.error("processAttachment(): MessagingException caught: " + contentType); logger.error("MessagingException caught", e); if (contentType.trim().toLowerCase().startsWith("multipart/") || contentType.trim().toLowerCase().startsWith("message/rfc822")) { aNode.setValue("003: MessagingException caught during process."); BodypartBean subNode = new BodypartBean("text/plain"); aNode.put(subNode); setAnodeValue(subNode, p); if (subNode.getValue() != null) { msgBean.getComponentsSize().add(Integer.valueOf(((byte[]) subNode.getValue()).length)); } subNode.setDisposition(aNode.getDisposition()); subNode.setDescription(aNode.getDescription()); } else { setAnodeValue(aNode, p); if (aNode.getValue() != null) { msgBean.getComponentsSize().add(Integer.valueOf(((byte[]) aNode.getValue()).length)); } } } catch (UnsupportedEncodingException e) { /* unsupported encoding found, use its raw data instead */ logger.error("processAttachment(): UnsupportedEncodingException caught: " + contentType); logger.error("UnsupportedEncodingException caught", e); if (contentType.trim().toLowerCase().startsWith("multipart/") || contentType.trim().toLowerCase().startsWith("message/rfc822")) { aNode.setValue("004: UnsupportedEncodingException caught during process."); BodypartBean subNode = new BodypartBean("text/plain"); aNode.put(subNode); setAnodeValue(subNode, p); if (subNode.getValue() != null) { msgBean.getComponentsSize().add(Integer.valueOf(((byte[]) subNode.getValue()).length)); } subNode.setDisposition(aNode.getDisposition()); subNode.setDescription(aNode.getDescription()); } else { setAnodeValue(aNode, p); if (aNode.getValue() != null) { msgBean.getComponentsSize().add(Integer.valueOf(((byte[]) aNode.getValue()).length)); } } } catch (IOException e) { /* * IOException caught during decoding, couldn't read the message * body. Use "-- Message body has been omitted --" as body text */ logger.error("processAttachment(): IOException caught: " + contentType); logger.error("IOException caught", e); if (contentType.trim().toLowerCase().startsWith("multipart/") || contentType.trim().toLowerCase().startsWith("message/rfc822")) { aNode.setValue("005: IOException caught during process."); BodypartBean subNode = new BodypartBean("text/plain"); aNode.put(subNode); subNode.setValue("-- Message body has been omitted --"); subNode.setDisposition(aNode.getDisposition()); subNode.setDescription(aNode.getDescription()); } else { aNode.setValue("-- Message body has been omitted --"); } } catch (Exception e) { /* all other unchecked exceptions */ logger.error("processAttachment(): Exception caught: " + contentType); logger.error("Exception caught", e); if (contentType.trim().toLowerCase().startsWith("multipart/") || contentType.trim().toLowerCase().startsWith("message/rfc822")) { aNode.setValue("006: Exception caught during process."); BodypartBean subNode = new BodypartBean("text/plain"); aNode.put(subNode); setAnodeValue(subNode, p, "Unchecked Exception caught: " + e.toString()); subNode.setDisposition(aNode.getDisposition()); subNode.setDescription(aNode.getDescription()); } else { setAnodeValue(aNode, p, "Unchecked Exception caught: " + e.toString()); } } } // end of processAttachment private static void setAnodeValue(BodypartBean anode, Part p) { setAnodeValue(anode, p, "-- Message body has been omitted. Exception thrown from p.getInputStream() --"); } private static void setAnodeValue(BodypartBean anode, Part p, String errmsg) { try { anode.setValue((InputStream) p.getInputStream()); } catch (Exception e) { anode.setValue(errmsg); } } final static MailDateFormat mailDateFormat = new MailDateFormat(); private static java.util.Date getHeaderDate(String text) { if (StringUtil.isEmpty(text)) return null; try { java.util.Date date = mailDateFormat.parse(text); return date; } catch (ParseException e) { logger.warn("getHeaderDate() - ParseException caught parsing: " + text); } return null; } /* * check and reformat email address * * @param s - * email address * @return reformatted address */ private static String checkAddr(String s) { // do not append domain name by default return checkAddr(s, false); } /* * check and reformat email address * * @param s - * email address * @param needDomain - * true requires domain * @return reformatted address */ private static String checkAddr(String s, boolean needDomain) { if (s == null || s.trim().length() == 0) { return s; } try { InternetAddress.parse(s); } catch (javax.mail.internet.AddressException e) { logger.error("AddressException caught during parsing", e); return null; } String addr = s; // is this a name only address? if (needDomain && addr.indexOf("@") < 0) { int pos; if ((pos = addr.indexOf(">")) < 0) { // does it look like <user>? no - append default domain name addr += "@" + hostName; } else { addr = addr.substring(0, pos) + "@" + hostName + ">"; } } return addr; } /* * analyze "Received" header and retrieve address from the header * * @param received - * header * @return "for" address if found, null otherwise. */ private static String analyzeReceived(String received) { int semicolon_pos = -1; if (received != null && (semicolon_pos = received.indexOf(";")) > 0) { received = received.substring(0, semicolon_pos); received = received.replace('\n', ' ').replace('\r', ' ').replace('\t', ' '); // required fields int from_pos = received.indexOf("from "); int by_pos = received.indexOf(" by ", from_pos + 1); int low_pos = Math.min(from_pos, by_pos); int high_pos = Math.max(from_pos, by_pos); int max_pos = high_pos; // check optional fields int via_pos = received.indexOf(" via ", max_pos + 1); max_pos = Math.max(max_pos, via_pos); int with_pos = received.indexOf(" with ", max_pos + 1); max_pos = Math.max(max_pos, with_pos); int id_pos = received.indexOf(" id ", max_pos + 1); max_pos = Math.max(max_pos, id_pos); int for_pos = received.indexOf(" for ", max_pos + 1); if (low_pos >= 0 && for_pos > high_pos) { return received.substring(for_pos + 4); } else if (by_pos >= 0 && with_pos > by_pos && for_pos > with_pos) { // AOL or Google - "received" could only contain "by" and // "with", but no "from" return received.substring(for_pos + 4); } else if (low_pos >= 0 && max_pos > high_pos) { // found optional field // address may have a display name and the display name may // contain one of the search keys used by the optional fields // (indexOf()) for_pos = received.lastIndexOf(" for "); if (for_pos > high_pos) { return received.substring(for_pos + 4); } } } return null; } /* * locate the file name from content-type. * * @param ctype - * content type * @return file name extracted from the content type */ private static String getFileName(String ctype) { String desc = null; if (ctype != null && ctype.indexOf("name=") >= 0) { desc = ctype.substring(ctype.indexOf("name=") + 5); if (desc != null && desc.indexOf(";") > 0) desc = desc.substring(0, desc.indexOf(";")); } return desc; } /** * convert MessageBean to JavaMail MimeMessage * * @param msgBean - * a MessageBean object * @return JavaMail Message * @throws MessagingException * @throws IOException */ public static Message beanToMime(MessageBean msgBean) throws MessagingException, IOException { javax.mail.Session session = Session.getDefaultInstance(System.getProperties()); if (debugSession) session.setDebug(true); Message msg = new MimeMessage(session); // First Set All Headers from a header List List<MsgHeader> headers = msgBean.getHeaders(); if (headers != null) { for (int i = 0; i < headers.size(); i++) { MsgHeader header = headers.get(i); if (!getReservedHeaders().contains(header.getName())) { msg.setHeader(header.getName(), header.getValue()); } if (isDebugEnabled) { logger.debug("beanToMime() - Header Line - " + header.getName() + ": " + header.getValue()); } } } // override certain headers with the data from MesssageBean if (msgBean.getFrom() != null) { for (int i = 0; i < msgBean.getFrom().length; i++) { // just for safety if (msgBean.getFrom()[i] != null) { msg.setFrom(msgBean.getFrom()[i]); break; } } } else { logger.warn("beanToMime() - MessageBean.getFrom() returns a null"); msg.setFrom(); } if (msgBean.getTo() != null) { msg.setRecipients(Message.RecipientType.TO, msgBean.getTo()); } else { logger.warn("beanToMime() - MessageBean.getTo() returns a null"); } if (msgBean.getCc() != null) { msg.setRecipients(Message.RecipientType.CC, msgBean.getCc()); } if (msgBean.getBcc() != null) { msg.setRecipients(Message.RecipientType.BCC, msgBean.getBcc()); } if (msgBean.getReplyto() != null) { msg.setReplyTo(msgBean.getReplyto()); } if (msgBean.getReturnPath() != null && msgBean.getReturnPath().trim().length() > 0) { msg.setHeader(RETURN_PATH, msgBean.getReturnPath()); } msg.setHeader(XHEADER_PRIORITY, getMsgPriority(msgBean.getPriority())); if (msgBean.getXmailer() != null && msgBean.getXmailer().length > 0) { msg.setHeader(XHEADER_MAILER, msgBean.getXmailer()[0]); } msg.setSentDate(new Date()); msg.setSubject(msgBean.getSubject() == null ? "" : msgBean.getSubject()); // construct message body part List<BodypartBean> aNodes = msgBean.getNodes(); if (msgBean.getMimeType().startsWith("multipart")) { Multipart mp = new MimeMultipart(msgBean.getMimeSubType()); msg.setContent(mp); constructMultiPart(mp, (BodypartBean) msgBean, 0); } else if (aNodes != null && aNodes.size() > 0) { Multipart mp = new MimeMultipart("mixed"); // make up a default msg.setContent(mp); if (msgBean.getValue()!=null) { BodyPart bp = new MimeBodyPart(); mp.addBodyPart(bp); constructSinglePart(bp, (BodypartBean) msgBean, 0); } constructMultiPart(mp, (BodypartBean) msgBean, 0); } else { constructSinglePart(msg, (BodypartBean) msgBean, 0); } msg.saveChanges(); // please remember to save the message return msg; } /** * create MessageBean from SMTP raw stream * @param mailStream * @return a MessageBean * @throws MessagingException */ public static MessageBean createMessageBeanFromStream(byte[] mailStream) throws MessagingException { Message msg = createMimeMessageFromStream(mailStream); try { MessageBean msgBean = mimeToBean(msg); return msgBean; } catch (IOException e) { logger.error("IOException caught", e); throw new MessagingException(e.toString()); } } /** * create JavaMail Message from SMTP raw stream * * @param mailStream * @return a JavaMail Message * @throws MessagingException */ public static Message createMimeMessageFromStream(byte[] mailStream) throws MessagingException { javax.mail.Session session = Session.getDefaultInstance(System.getProperties()); session.setDebug(true); ByteArrayInputStream bais = new ByteArrayInputStream(mailStream); Message msg = new MimeMessage(session, bais); msg.saveChanges(); session.setDebug(debugSession); return msg; } private static void constructMultiPart(Multipart mp, BodypartBean aNode, int level) throws MessagingException, IOException { if (isDebugEnabled) { logger.debug("constructMultiPart() - MultipartHL - " + StringUtil.getPeriods(level) + "Content Type: " + mp.getContentType()); } List<BodypartBean> aNodes = aNode.getNodes(); for (int i = 0; aNodes != null && i < aNodes.size(); i++) { BodypartBean subNode = aNodes.get(i); if (subNode.getMimeType().startsWith("multipart")) { Multipart subMp = new MimeMultipart(subNode.getMimeSubType()); BodyPart multiBody = new MimeBodyPart(); multiBody.setContent(subMp); mp.addBodyPart(multiBody); constructMultiPart(subMp, subNode, level + 1); } else { BodyPart bodyPart = new MimeBodyPart(); mp.addBodyPart(bodyPart); constructSinglePart(bodyPart, subNode, level + 1); } } } private static final Set<String> reservedHeaders = new HashSet<String>(); private static Set<String> getReservedHeaders() { if (reservedHeaders.isEmpty()) { reservedHeaders.add("Delivered-To"); reservedHeaders.add("Received"); reservedHeaders.add("Message-ID"); reservedHeaders.add("Subject"); reservedHeaders.add("Return-Path"); //reservedHeaders.add("User-Agent"); } return reservedHeaders; } private static void constructSinglePart(Part part, BodypartBean aNode, int level) throws MessagingException, IOException { // Set All Headers List<MsgHeader> headers = aNode.getHeaders(); if (headers != null) { for (int i = 0; i < headers.size(); i++) { MsgHeader header = headers.get(i); if (!getReservedHeaders().contains(header.getName())) { part.setHeader(header.getName(), header.getValue()); } if (isDebugEnabled) { logger.debug("constructSinglePart() - Header Line - " + StringUtil.getPeriods(level) + header.getName() + ": " + header.getValue()); } } } part.setDisposition(aNode.getDisposition()); part.setDescription(aNode.getDescription()); if (aNode.getMimeType().startsWith("text")) { part.setContent(new String(aNode.getValue()), aNode.getContentType()); if (aNode.getMimeType().startsWith("text/html")) { if (aNode.getDisposition() == null) { // part.setDisposition(Part.INLINE); /* Do not uncomment above line, as some SMTP server will insert "-----Inline Attachment Follows-----" at the beginning of the message. */ } } } else { if (aNode.getDescription() == null) { // not sure why do this, consistency? part.setDescription(getFileName(aNode.getContentType())); } ByteArrayDataSource bads = new ByteArrayDataSource(aNode.getValue(), aNode .getContentType()); part.setDataHandler(new DataHandler(bads)); } } private static String getMsgPriority(String[] priority) { String outPriority = "2 (Normal)"; if (priority != null && priority[0] != null) { String in_p = priority[0].trim(); if (in_p.equalsIgnoreCase("HIGH")) outPriority = "1 (High)"; else if (in_p.equalsIgnoreCase("NORM")) outPriority = "2 (Normal)"; else if (in_p.equalsIgnoreCase("LOW")) outPriority = "3 (Low)"; } return (outPriority); } private static List<String> getMessageBeanMethodNames() { Method methods[] = MessageBean.class.getMethods(); List<String> methodNameList = new ArrayList<String>(); for (int i = 0; i < methods.length; i++) { Method method = (Method) methods[i]; Class<?> parmTypes[] = method.getParameterTypes(); int mod = method.getModifiers(); if (Modifier.isPublic(mod) && !Modifier.isAbstract(mod) && !Modifier.isStatic(mod)) { if (method.getName().length() > 3 && method.getName().startsWith("get") && parmTypes.length == 0) { String name = method.getName().substring(3); if (method.getReturnType().getName().equals("java.lang.String") || method.getReturnType().getName().equals("java.lang.Long")) { // ignore following methods if ("BodyContentType".equals(name)) continue; if (name.startsWith("Dsn")) continue; if (name.startsWith("Des")) continue; if (name.startsWith("Dia")) continue; if (name.startsWith("Dis")) continue; if (name.endsWith("AsString")) continue; // end of ignore methodNameList.add(name); } else if (method.getReturnType().getCanonicalName().equals("javax.mail.Address[]")) { methodNameList.add(name); } } } } Collections.sort(methodNameList); return methodNameList; } private static String invokeMethod(MessageBean msgBean, String name) { if (msgBean == null || name == null) { logger.warn("invokeMethod() - Either msgBean or name is null."); return null; } if (name.startsWith("Email_")) { // strip off prefix name = name.substring(6); } name = "get" + name; try { if (isDebugEnabled) logger.debug("invoking method: " + name + "()"); Method method = msgBean.getClass().getMethod(name, (Class[])null); Object obj = method.invoke(msgBean, (Object[])null); if (obj instanceof String) { return (String) obj; } else if (obj instanceof Long) { return ((Long)obj).toString(); } else if (obj instanceof Address[]) { return StringUtil.addrToString((Address[])obj); } else if (obj != null) { logger.warn("invokeMethod() - invalid return type: " + obj.getClass().getName()); } } catch (Exception e) { logger.error("invokeMethod() - Exception caught", e); } return null; } public static void main(String[] args) { List<String> methodNameList = getMessageBeanMethodNames(); StringBuffer sb = new StringBuffer(); for (int i=0; i<methodNameList.size(); i++) { sb.append(methodNameList.get(i) + LF); } System.out.println(sb.toString()); MessageBean msgBean = new MessageBean(); msgBean.setSubject("test subject"); msgBean.setBody("test body text"); System.out.println("Invoke getBody(): " + invokeMethod(msgBean, "Body")); System.out.println("Invoke getSubject(): " + invokeMethod(msgBean, "Subject")); try { Message msg = beanToMime(msgBean); MessageBean bean = mimeToBean(msg); System.out.println("########## MessageBean Before:"); System.out.println(msgBean); System.out.println("########## MessageBean After:"); System.out.println(bean); } catch (Exception e) { e.printStackTrace(); } } }
getFileName(String ctype) needs to be protected, not private, since it's used by BodypartUtil
ReplyDelete