/*
* JBoss, Home of Professional Open Source
* Copyright 2006, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This 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 2.1 of
* the License, or (at your option) any later version.
*
* This software 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 software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.soa.esb.helpers;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.StringTokenizer;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.mail.Address;
import javax.mail.Authenticator;
import javax.mail.BodyPart;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import org.apache.log4j.Logger;
import org.jboss.internal.soa.esb.assertion.AssertArgument;
import org.jboss.soa.esb.common.Configuration;
import org.jboss.soa.esb.common.Environment;
import org.jboss.soa.esb.services.security.PasswordUtil;
import org.jboss.soa.esb.util.Util;
/**
* Simplifies sending of e-mails with attachments from java.
* <p/>
* The message is sent via a subsequent call to the {@link #sendMessage()}
* method.
*
* <h3 id="p_oMessageParms">Message Parmeters</h3>
* The class constructor is supplied the following parameters via a
* {@link org.jboss.soa.esb.helpers.ConfigTree} instance:
* <ol>
* <li><b>FROM</b>: valid e-mail address</li>
* <li><b>SENDTO</b>: valid comma separated list of e-mail addresses</li>
* <li><b>SUBJECT</b>: free text that will appear as subject of the e-mail</li>
* <li><b>MESSAGE</b>: free text that will appear as contents of the e-mail</li>
* <li><b>ATTACH</b>: list (as Text children) of valid accessible filenames to
* attach to the e-mail</li>
* </ol>
* All parameters except 'ATTACH' are attributes of the ConfigTree argument. ATTACH
* parameters (attachments) are added as child ConfigTrees of the supplied
* ConfigTree.
*
* @author Heuristica - Buenos Aires - Argentina
*/
public class Email {
/**
* Class logger.
*/
private static Logger logger = Logger.getLogger(Email.class);
public static final String HOST = "host";
public static final String PORT = "port";
public static final String USERNAME = "username";
public static final String PASSWORD = "password";
public static final String AUTH = "auth";
/**
* ConfigTree attribute : will be the contents of the From: field in the
* outgoing message
*/
public static final String FROM = "from";
/**
* ConfigTree attribute : comma separated list of recipients
*/
public static final String SENDTO = "sendTo";
/**
* ConfigTree attribute : comma separated list of Copy recipients
*/
public static final String COPYTO = "ccTo";
/**
* ConfigTree attribute : will be the contents of the Subject: field in the
* outgoing message
*/
public static final String SUBJECT = "subject";
/**
* ConfigTree attribute : Value of this attribute will be the content of the
* e-mail's text
*/
public static final String ATTACH = "attachment";
/**
* ConfigTree child Text elements : Each child represents the name of a file
* to be attached in the outgoing message
*/
public static final String MESSAGE = "message";
/**
* will be the contents of the From: field in the outgoing message
*/
private String from = null;
/**
* comma separated list of recipients
*/
private String sendTo = null;
/**
* comma separated list of copy recipients
*/
private String copyTo = null;
/**
* This attribute will be the content of the subject of the mail message
*/
private String subject = null;
/**
* Comma separated list of attachment file names
*/
private String[] attachments = null;
/**
* List of MimeBodyParts containing the attachments that should be added to the mail. The attachments are added using the {@link #addAttachment(InputStream, String)} method.
*/
private List<MimeBodyPart> attachmentParts = new ArrayList<MimeBodyPart>();
/**
* Value of this attribute will be the content of the e-mail's text
*/
private String message = null;
/**
* The message to b sent via a subsequent call to {@link #sendMessage()}.
*/
private MimeMessage mailMessage;
/**
* The mail Session to use
*/
private final Session oMailSess;
/**
* Public constructor.
* <p/>
* Initialises the mail server session and creates the mail message using
* the information provided in the p_oMessageParms
* <a href="p_oMessageParms">listed above</a>.
* <p/>
* The message is sent via a subsequent call to the {@link #sendMessage()}
* method.
*
* @throws MessagingException
* @throws AddressException
* @see ConfigTree
*
*/
public Email() throws AddressException, MessagingException {
// REVIEW: How about typing the parameters a little better??? Looks as though that might have a huge ripple-on effect accross the codebase!!
// REVIEW: Does the session need to ne initialised every time???
this.oMailSess = initMailServerSession();
// Message can be "sent" via a subsequent call to the sendMessage method!
} // __________________________________
/**
* Initialises the mail server session to the specified smtp host and port and uses
* SMTP Authentication.
*
* @param host The host of the smtp server. Must not be null or an empty String.
* @param port The port that the smtp server is running on.
* @param username The username on the smtp server. Can be null.
* @param password The password for username on the smtp server. Cannot be null if username was specified.
* @param auth If true will attempt to authenticate the user using the AUTH command.
*
* @throws AddressException
* @throws MessagingException
*/
public Email(final String host, final int port, final String username, final String password, final boolean auth) throws AddressException, MessagingException {
AssertArgument.isNotNullAndNotEmpty(host, "host");
try
{
this.oMailSess = initMailServerSession(host, port, username, password, auth);
}
catch (IOException e)
{
throw new MessagingException(e.getMessage(), e);
}
}
/**
* Send the mail message associated with this instance.
* @throws MessagingException Unable to transport the message associated with this
* Email instance.
*/
public void sendMessage() throws MessagingException {
Transport.send(this.getMailMessage());
}
/**
* Get the {@link MimeMessage mail message} associated with this EsbMail
* instance.
* @return The mailMessage property value.
*/
public MimeMessage getMailMessage() throws MessagingException {
if (this.mailMessage == null){
this.mailMessage = this.createMailMessage(this.oMailSess);
}
return mailMessage;
}
/**
* @return Returns the attachments.
*/
public String[] getAttachments() {
return this.attachments;
}
/**
* @param attachments The attachments to set.
*/
public void setAttachments(String[] attachments) {
this.attachments = attachments;
}
/**
* @param is the InputStream containing the attachment to set
* @param attachmentName the filename of the attachment in the mail
* @throws MessagingException
* @throws IOException
*/
public void addAttachment(final InputStream is, final String attachmentName) throws MessagingException, IOException {
MimeBodyPart part = new MimeBodyPart();
// This part should be replaced by javax.mail.util.ByteArrayDataSource when upgrading to use java mail API 1.4 or later
part.setDataHandler(new DataHandler(new DataSource() {
private ByteArrayOutputStream bos;
{
bos = new ByteArrayOutputStream();
int read;
byte[] buff = new byte[1024*8];
while((read = is.read(buff)) != -1) {
bos.write(buff, 0, read);
}
bos.close();
}
public String getContentType() {
return "application/octet-stream";
}
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(bos.toByteArray());
}
public String getName() {
return attachmentName;
}
public OutputStream getOutputStream() throws IOException {
throw new IOException("Cannot write to this read-only resource");
}
}));
part.setFileName(attachmentName);
attachmentParts.add(part);
}
/**
* @return Returns the copyTo.
*/
public String getCopyTo() {
return this.copyTo;
}
/**
* @param copyTo The copyTo to set.
*/
public void setCopyTo(String copyTo) {
this.copyTo = copyTo;
}
/**
* @return Returns the from.
*/
public String getFrom() {
return this.from;
}
/**
* @param from The from to set.
*/
public void setFrom(String from) {
this.from = from;
}
/**
* @return Returns the sendTo.
*/
public String getSendTo() {
return this.sendTo;
}
/**
* @param sendTo The sendTo to set.
*/
public void setSendTo(String sendTo) {
this.sendTo = sendTo;
}
/**
* @return Returns the subject.
*/
public String getSubject() {
return this.subject;
}
/**
* @param subject The subject to set.
*/
public void setSubject(String subject) {
this.subject = subject;
}
/**
* @return Returns the message.
*/
public String getMessage() {
return this.message;
}
/**
* @param message The message to set.
*/
public void setMessage(String message) {
this.message = message;
}
/**
* Create the mail message instance to be {@link #sendMessage() sent}.
* @param p_oMessageParms <a href="p_oMessageParms">Message parameters.</a>.
* @param oMailSess Mail session on which the message is to be transported.
* @return The message.
* @throws AddressException
* @throws MessagingException
*/
private MimeMessage createMailMessage(Session oMailSess) throws AddressException, MessagingException {
MimeMessage oMessage = new MimeMessage(oMailSess);
// Populate the message with the data supplied in the p_oMessageParms ConfigTree.
addMessageAddressing(oMessage);
addMessageSubject( oMessage);
addMessageContent(oMessage);
return oMessage;
}
/**
* Add the message addressing information to the message.
* @param p_oMessageParms <a href="p_oMessageParms">Message parameters.</a>.
* @param oMessage The message.
* @throws AddressException
* @throws MessagingException
*/
private void addMessageAddressing(MimeMessage oMessage) throws AddressException, MessagingException {
InternetAddress oFrom = new InternetAddress(this.from);
oMessage.setFrom(oFrom);
oMessage.setReplyTo(new Address[] { oFrom });
InternetAddress[] oaTo =
InternetAddress.parse(this.sendTo);
for (int i1 = 0; i1 < oaTo.length; i1++) {
oMessage.addRecipient(MimeMessage.RecipientType.TO, oaTo[i1]);
}
if (null != this.copyTo) {
oaTo = InternetAddress.parse(this.copyTo);
for (int i1 = 0; i1 < oaTo.length; i1++) {
oMessage.addRecipient(MimeMessage.RecipientType.CC, oaTo[i1]);
}
}
}
/**
* Add the message Subject to the message.
* @param p_oMessageParms <a href="p_oMessageParms">Message parameters.</a>.
* @param oMessage The message.
* @throws MessagingException
*/
private void addMessageSubject(MimeMessage oMessage) throws MessagingException {
if (null != this.subject) {
oMessage.setSubject(this.subject);
}
}
/**
* Add the message content (body, attachments etc) to from the
* message parameters to the message.
* @param p_oMessageParms <a href="p_oMessageParms">Message parameters.</a>.
* @param oMessage The message.
* @throws MessagingException
*/
private void addMessageContent(MimeMessage oMessage) throws MessagingException {
BodyPart oBodyP = new MimeBodyPart();
Multipart oMultiP = new MimeMultipart();
oMultiP.addBodyPart(oBodyP);
oMessage.setContent(oMultiP);
if (null == this.message) {
this.message = "";
}
oBodyP.setText(this.message + "\n");
if (null != attachments) {
for (int i1 = 0; i1 < this.attachments.length; i1++) {
oMultiP.addBodyPart(oBodyP = new MimeBodyPart());
String sFile = this.attachments[i1];
oBodyP.setDataHandler(new DataHandler(new FileDataSource(sFile)));
oBodyP.setFileName(sFile.substring(1 + sFile.lastIndexOf("\\")));
}
}
for (MimeBodyPart part : attachmentParts) {
oMultiP.addBodyPart(part);
}
}
/**
* Initialise an authenticated {@link javax.mail.Session} with the mail server.
*
* @param host The host of the smtp server.
* @param port The port that the smtp server is running on.
* @param username The username on the smtp server
* @param password The password for username on the smtp server.
* @param auth If true will attempt to authenticate the user using the AUTH command. This will set the property 'mail.smtp.auth'.
*
* @return The {@link javax.mail.Session}.
* @throws IOException
*/
private Session initMailServerSession(final String host, final int port, final String username, final String password, final boolean auth) throws IOException {
Authenticator authenticator = null;
if (!Util.isNullString(username)) {
String pw = password ;
if (PasswordUtil.isPasswordFile(password))
{
pw = new PasswordUtil(password).getPasswordAsString();
}
authenticator = new MyAuth(username, pw);
}
final Properties properties = new Properties();
properties.setProperty("mail.smtp.host", host);
properties.setProperty("mail.smtp.port", String.valueOf(port));
properties.setProperty("mail.smtp.auth", String.valueOf(auth));
logger.debug("Initialising mail server sesson. Properties: " + properties);
return Session.getInstance(properties, authenticator);
}
/**
* Initialise an authenticated {@link javax.mail.Session} with the mail server.
* @return The {@link javax.mail.Session}.
*/
private Session initMailServerSession() {
Authenticator oAuth = null;
String sSmtpUser = Configuration.getSmtpUsername();
if (! Util.isNullString(sSmtpUser)) {
oAuth = new MyAuth(sSmtpUser, Configuration.getSmtpPassword());
}
Properties oMailP = new Properties();
oMailP.setProperty("mail.smtp.host", Configuration.getSmtpHost());
String sAuth = Configuration.getSmtpAuth();
if(sAuth != null) {
if(sAuth.trim().equals("")) {
logger.warn("'" + Environment.SMTP_AUTH + "' set to an empty value.");
}
oMailP.setProperty("mail.smtp.auth", sAuth);
}
try {
String sPort = Configuration.getSmtpPort();
this.from = Configuration.getSmtpFrom();
Integer.parseInt(sPort);
oMailP.setProperty("mail.smtp.port", sPort);
}
catch (Exception e) { /* OK just leave standard port */ }
logger.info("Initialising mail server sesson. Properties: " + oMailP);
javax.mail.Session oMailSess = javax.mail.Session.getInstance(oMailP, oAuth);
return oMailSess;
}
/**
* Method parsing filename from a string containing a comma separated list of filenames
* @param attachments the string containing the comma separated list of filenames
* @return a String array containing an entry for every filename in the given comma separated list
*/
@SuppressWarnings("unused")
private String[] getFileNamesAsArray(String attachments) {
if (attachments.indexOf(',') != -1){
StringTokenizer st = new StringTokenizer(attachments,",");
String[] attachmentFileNames = new String[st.countTokens()];
int index = 0;
while (st.hasMoreTokens()) {
attachmentFileNames[index] = st.nextToken();
index++;
}
return attachmentFileNames;
}
return new String[]{attachments};
}
private static class MyAuth extends Authenticator {
private String m_sUser, m_sPwd;
private MyAuth(String p_sU, String p_sP) {
m_sUser = p_sU;
m_sPwd = p_sP;
}
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(m_sUser, m_sPwd);
} // ________________________________
} // ______________________________________________________
} // ____________________________________________________________________________