EMailMessage.java revision a456cceb195b5f15ed161b7337d02ff8656ae59b
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
* or http://forgerock.org/license/CDDLv1.0.html.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at legal-notices/CDDLv1_0.txt.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
*
* Copyright 2006-2008 Sun Microsystems, Inc.
* Portions Copyright 2014-2015 ForgeRock AS
* Portions copyright 2015 Edan Idzerda
*/
package org.opends.server.util;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.mail.MessagingException;
import javax.mail.SendFailedException;
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 org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.LocalizableMessageBuilder;
import org.opends.server.core.DirectoryServer;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import com.forgerock.opendj.cli.ArgumentException;
import com.forgerock.opendj.cli.ArgumentParser;
import com.forgerock.opendj.cli.BooleanArgument;
import com.forgerock.opendj.cli.CommonArguments;
import com.forgerock.opendj.cli.StringArgument;
import static org.opends.messages.ToolMessages.*;
import static org.opends.messages.UtilityMessages.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
/**
* This class defines an e-mail message that may be sent to one or more
* recipients via SMTP. This is a wrapper around JavaMail to make this process
* more convenient and fit better into the Directory Server framework.
*/
@org.opends.server.types.PublicAPI(
stability=org.opends.server.types.StabilityLevel.VOLATILE,
mayInstantiate=true,
mayExtend=false,
mayInvoke=true)
public final class EMailMessage
{
private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
/** The addresses of the recipients to whom this message should be sent. */
private List<String> recipients;
/** The set of attachments to include in this message. */
private LinkedList<MimeBodyPart> attachments;
/** The MIME type for the message body. */
private String bodyMIMEType;
/** The address of the sender for this message. */
private String sender;
/** The subject for the mail message. */
private String subject;
/** The body for the mail message. */
private LocalizableMessageBuilder body;
/**
* Creates a new e-mail message with the provided information.
*
* @param sender The address of the sender for the message.
* @param recipient The address of the recipient for the message.
* @param subject The subject to use for the message.
*/
public EMailMessage(String sender, String recipient, String subject)
{
this.sender = sender;
this.subject = subject;
recipients = CollectionUtils.newArrayList(recipient);
body = new LocalizableMessageBuilder();
attachments = new LinkedList<>();
bodyMIMEType = "text/plain";
}
/**
* Creates a new e-mail message with the provided information.
*
* @param sender The address of the sender for the message.
* @param recipients The addresses of the recipients for the message.
* @param subject The subject to use for the message.
*/
public EMailMessage(String sender, List<String> recipients,
String subject)
{
this.sender = sender;
this.recipients = recipients;
this.subject = subject;
body = new LocalizableMessageBuilder();
attachments = new LinkedList<>();
bodyMIMEType = "text/plain";
}
/**
* Retrieves the sender for this message.
*
* @return The sender for this message.
*/
public String getSender()
{
return sender;
}
/**
* Specifies the sender for this message.
*
* @param sender The sender for this message.
*/
public void setSender(String sender)
{
this.sender = sender;
}
/**
* Retrieves the set of recipients for this message. This list may be
* directly manipulated by the caller.
*
* @return The set of recipients for this message.
*/
public List<String> getRecipients()
{
return recipients;
}
/**
* Specifies the set of recipients for this message.
*
* @param recipients The set of recipients for this message.
*/
public void setRecipients(ArrayList<String> recipients)
{
this.recipients = recipients;
}
/**
* Adds the specified recipient to this message.
*
* @param recipient The recipient to add to this message.
*/
public void addRecipient(String recipient)
{
recipients.add(recipient);
}
/**
* Retrieves the subject for this message.
*
* @return The subject for this message.
*/
public String getSubject()
{
return subject;
}
/**
* Specifies the subject for this message.
*
* @param subject The subject for this message.
*/
public void setSubject(String subject)
{
this.subject = subject;
}
/**
* Retrieves the MIME Type for the body of this message
*
* @return The MIME Type for this message
*/
public String getBodyMIMEType()
{
return bodyMIMEType;
}
/**
* Specifies the MIME Type for the body of this message
*
* @param bodyMIMEType The MIME Type for this message
*/
public void setBodyMIMEType(String bodyMIMEType)
{
this.bodyMIMEType = bodyMIMEType;
}
/**
* Retrieves the body for this message. It may be directly manipulated by the
* caller.
*
* @return The body for this message.
*/
public LocalizableMessageBuilder getBody()
{
return body;
}
/**
* Specifies the body for this message.
*
* @param body The body for this message.
*/
public void setBody(LocalizableMessageBuilder body)
{
this.body = body;
}
/**
* Specifies the body for this message.
*
* @param body The body for this message.
*/
public void setBody(LocalizableMessage body)
{
this.body = new LocalizableMessageBuilder(body);
}
/**
* Appends the provided text to the body of this message.
*
* @param text The text to append to the body of the message.
*/
public void appendToBody(String text)
{
body.append(text);
}
/**
* Retrieves the set of attachments for this message. This list may be
* directly modified by the caller if desired.
*
* @return The set of attachments for this message.
*/
public LinkedList<MimeBodyPart> getAttachments()
{
return attachments;
}
/**
* Adds the provided attachment to this mail message.
*
* @param attachment The attachment to add to this mail message.
*/
public void addAttachment(MimeBodyPart attachment)
{
attachments.add(attachment);
}
/**
* Adds an attachment to this mail message with the provided text.
*
* @param attachmentText The text to include in the attachment.
*
* @throws MessagingException If there is a problem of some type with the
* attachment.
*/
public void addAttachment(String attachmentText)
throws MessagingException
{
MimeBodyPart attachment = new MimeBodyPart();
attachment.setText(attachmentText);
attachments.add(attachment);
}
/**
* Adds the provided attachment to this mail message.
*
* @param attachmentFile The file containing the attachment data.
*
* @throws MessagingException If there is a problem of some type with the
* attachment.
*/
public void addAttachment(File attachmentFile)
throws MessagingException
{
MimeBodyPart attachment = new MimeBodyPart();
FileDataSource dataSource = new FileDataSource(attachmentFile);
attachment.setDataHandler(new DataHandler(dataSource));
attachment.setFileName(attachmentFile.getName());
attachments.add(attachment);
}
/**
* Attempts to send this message to the intended recipient(s). This will use
* the mail server(s) defined in the Directory Server mail handler
* configuration. If multiple servers are specified and the first is
* unavailable, then the other server(s) will be tried before returning a
* failure to the caller.
*
* @throws MessagingException If a problem occurred while attempting to send
* the message.
*/
public void send()
throws MessagingException
{
send(DirectoryServer.getMailServerPropertySets());
}
/**
* Attempts to send this message to the intended recipient(s). If multiple
* servers are specified and the first is unavailable, then the other
* server(s) will be tried before returning a failure to the caller.
*
* @param mailServerPropertySets A list of property sets providing
* information about the mail servers to use
* when sending the message.
*
* @throws MessagingException If a problem occurred while attempting to send
* the message.
*/
public void send(List<Properties> mailServerPropertySets)
throws MessagingException
{
// Get information about the available mail servers that we can use.
MessagingException sendException = null;
for (Properties props : mailServerPropertySets)
{
// Get a session and use it to create a new message.
Session session = Session.getInstance(props);
MimeMessage message = new MimeMessage(session);
message.setSubject(subject);
message.setSentDate(new Date());
// Add the sender address. If this fails, then it's a fatal problem we'll
// propagate to the caller.
try
{
message.setFrom(new InternetAddress(sender));
}
catch (MessagingException me)
{
logger.traceException(me);
LocalizableMessage msg = ERR_EMAILMSG_INVALID_SENDER_ADDRESS.get(sender, me.getMessage());
throw new MessagingException(msg.toString(), me);
}
// Add the recipient addresses. If any of them fail, then that's a fatal
// problem we'll propagate to the caller.
InternetAddress[] recipientAddresses =
new InternetAddress[recipients.size()];
for (int i=0; i < recipientAddresses.length; i++)
{
String recipient = recipients.get(i);
try
{
recipientAddresses[i] = new InternetAddress(recipient);
}
catch (MessagingException me)
{
logger.traceException(me);
LocalizableMessage msg = ERR_EMAILMSG_INVALID_RECIPIENT_ADDRESS.get(recipient, me.getMessage());
throw new MessagingException(msg.toString(), me);
}
}
message.setRecipients(
javax.mail.Message.RecipientType.TO,
recipientAddresses);
// If we have any attachments, then the whole thing needs to be
// multipart. Otherwise, just set the text of the message.
if (attachments.isEmpty())
{
message.setContent(body.toString(), bodyMIMEType);
}
else
{
MimeMultipart multiPart = new MimeMultipart();
MimeBodyPart bodyPart = new MimeBodyPart();
bodyPart.setText(body.toString());
multiPart.addBodyPart(bodyPart);
for (MimeBodyPart attachment : attachments)
{
multiPart.addBodyPart(attachment);
}
message.setContent(multiPart);
}
// Try to send the message. If this fails, it can be a complete failure
// or a partial one. If it's a complete failure then try rolling over to
// the next server. If it's a partial one, then that likely means that
// the message was sent but one or more recipients was rejected, so we'll
// propagate that back to the caller.
try
{
Transport.send(message);
return;
}
catch (SendFailedException sfe)
{
logger.traceException(sfe);
// We'll ignore this and hope that another server is available. If not,
// then at least save the exception so that we can throw it if all else
// fails.
if (sendException == null)
{
sendException = sfe;
}
}
// FIXME -- Are there any other types of MessagingException that we might
// want to catch so we could try again on another server?
}
// If we've gotten here, then we've tried all of the servers in the list and
// still failed. If we captured an earlier exception, then throw it.
// Otherwise, throw a generic exception.
if (sendException == null)
{
LocalizableMessage message = ERR_EMAILMSG_CANNOT_SEND.get();
throw new MessagingException(message.toString());
}
else
{
throw sendException;
}
}
/**
* Provide a command-line mechanism for sending an e-mail message via SMTP.
*
* @param args The command-line arguments provided to this program.
*/
public static void main(String[] args)
{
LocalizableMessage description = INFO_EMAIL_TOOL_DESCRIPTION.get();
ArgumentParser argParser = new ArgumentParser(EMailMessage.class.getName(),
description, false);
BooleanArgument showUsage = null;
StringArgument attachFile = null;
StringArgument bodyFile = null;
StringArgument host = null;
StringArgument from = null;
StringArgument subject = null;
StringArgument to = null;
try
{
host = new StringArgument("host", 'h', "host", true, true, true,
INFO_HOST_PLACEHOLDER.get(), "127.0.0.1", null,
INFO_EMAIL_HOST_DESCRIPTION.get());
argParser.addArgument(host);
from = new StringArgument("from", 'f', "from", true, false, true,
INFO_ADDRESS_PLACEHOLDER.get(), null, null,
INFO_EMAIL_FROM_DESCRIPTION.get());
argParser.addArgument(from);
to = new StringArgument("to", 't', "to", true, true, true,
INFO_ADDRESS_PLACEHOLDER.get(),
null, null, INFO_EMAIL_TO_DESCRIPTION.get());
argParser.addArgument(to);
subject = new StringArgument("subject", 's', "subject", true, false, true,
INFO_SUBJECT_PLACEHOLDER.get(), null, null,
INFO_EMAIL_SUBJECT_DESCRIPTION.get());
argParser.addArgument(subject);
bodyFile = new StringArgument("bodyfile", 'b', "body", true, true, true,
INFO_PATH_PLACEHOLDER.get(), null, null,
INFO_EMAIL_BODY_DESCRIPTION.get());
argParser.addArgument(bodyFile);
attachFile = new StringArgument("attachfile", 'a', "attach", false, true,
true, INFO_PATH_PLACEHOLDER.get(), null,
null,
INFO_EMAIL_ATTACH_DESCRIPTION.get());
argParser.addArgument(attachFile);
showUsage = CommonArguments.getShowUsage();
argParser.addArgument(showUsage);
argParser.setUsageArgument(showUsage);
}
catch (ArgumentException ae)
{
System.err.println(ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage()));
System.exit(1);
}
try
{
argParser.parseArguments(args);
}
catch (ArgumentException ae)
{
argParser.displayMessageAndUsageReference(System.err, ERR_ERROR_PARSING_ARGS.get(ae.getMessage()));
System.exit(1);
}
if (showUsage.isPresent())
{
return;
}
LinkedList<Properties> mailServerProperties = new LinkedList<>();
for (String s : host.getValues())
{
Properties p = new Properties();
p.setProperty(SMTP_PROPERTY_HOST, s);
mailServerProperties.add(p);
}
EMailMessage message = new EMailMessage(from.getValue(), to.getValues(),
subject.getValue());
for (String s : bodyFile.getValues())
{
try
{
File f = new File(s);
if (! f.exists())
{
System.err.println(ERR_EMAIL_NO_SUCH_BODY_FILE.get(s));
System.exit(1);
}
BufferedReader reader = new BufferedReader(new FileReader(f));
while (true)
{
String line = reader.readLine();
if (line == null)
{
break;
}
message.appendToBody(line);
message.appendToBody("\r\n"); // SMTP says we should use CRLF.
}
reader.close();
}
catch (Exception e)
{
System.err.println(ERR_EMAIL_CANNOT_PROCESS_BODY_FILE.get(s,
getExceptionMessage(e)));
System.exit(1);
}
}
if (attachFile.isPresent())
{
for (String s : attachFile.getValues())
{
File f = new File(s);
if (! f.exists())
{
System.err.println(ERR_EMAIL_NO_SUCH_ATTACHMENT_FILE.get(s));
System.exit(1);
}
try
{
message.addAttachment(f);
}
catch (Exception e)
{
System.err.println(ERR_EMAIL_CANNOT_ATTACH_FILE.get(s,
getExceptionMessage(e)));
}
}
}
try
{
message.send(mailServerProperties);
}
catch (Exception e)
{
System.err.println(ERR_EMAIL_CANNOT_SEND_MESSAGE.get(
getExceptionMessage(e)));
System.exit(1);
}
}
}