Sometimes you just want to send a crapload of email from Salesforce.com. However, like every PaaS platform there are limits baked into the multi-tenant environment so you don't stomp on other tenants' resources. Salesforce.com limits you to 2000 emails per day for each Salesforce license. So if you don't have a lot of Salesforce licenses or a different kind of license, you may be out of luck if you want to send out large volumes. There are a few AppExchange products but they seem more targeted towards marketing purposes.
Google App Engine may be a good solution in this case. With Google App Engine quotas you get 7,000 Mail API calls per day free and can bump that up as high as 1.7M with a paid account.
Here's how to roll your own basic bulk emailer using Google App Engine. Take a look at the video below, but it essentially queries Force.com for records, uses the Google Mail Java API to send out individual emails and then send an administrator a notification via Jabber (Google Talk). You can schedule your application (essentially a Servlet) to run on a timed basis using the cron service.
All of this code is at this GitHub repo.
Most of the important code is in the MailServlet class.
package com.jeffdouglas.emailer;
[removed imports]
/**
* MailServlet.java - a simple, schedulable servlet for sending mail
* with salesforce.com
* @author Jeff Douglas
* @version 1.0
* @see http://code.google.com/appengine/docs/java/mail/overview.html
* for more details on using Mail with App Engine
*/
@SuppressWarnings("serial")
public class MailServlet extends HttpServlet {
private static final Logger logger = Logger.getLogger(ConnectionManager.class.getName());
private String jabberRecipient = "jeffdonthemic@gmail.com";
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
resp.setContentType("text/html");
String mailerMsg = "No contact found to email!!";
QueryResult result = null;
// get a reference to the salesforce connection
PartnerConnection connection = ConnectionManager.getConnectionManager().getConnection();
try {
// query for contacts based upon some criteria -- emailNotSent boolean
result = connection.query("Select Id, FirstName, LastName, Email " +
"FROM Contact Where Email = 'jeff@jeffdouglas.com' Limit 1");
} catch (ConnectionException e) {
e.printStackTrace();
logger.severe(e.getCause().toString());
} catch (NullPointerException npe) {
npe.printStackTrace();
logger.severe(npe.getCause().toString());
}
// if records were returned then send out email
if (result != null) {
for (SObject contact : result.getRecords()) {
// construct the 'name' for the email recipient
String contactName = contact.getField("FirstName").toString() + " " +
contact.getField("LastName").toString();
logger.info("Sending emil to " + contactName + " at " +
contact.getField("Email").toString());
/// send the email
mailerMsg = sendMail(contact.getField("Email").toString(), contactName);
// send a jabber notification of the status
sendJabberNotification(jabberRecipient, mailerMsg);
}
// TODO - make a call back into salesforce and update these records
// as having their emails sent. Implementation is up to you.
} else {
logger.warning("No results returned from salesforce");
}
resp.getOutputStream().println(mailerMsg);
}
/**
* Sends an email
* @param toAddress the email address of the recipient
* @param toName the name that appears for the recipeint in their email client
* @return A String representing the status of the email sent
*/
private String sendMail(String toAddress, String toName) {
String msg = "Email sent successfully to " + toAddress;
Properties props = new Properties();
Session session = Session.getDefaultInstance(props, null);
String messageBody = "This is the body of my email";
try {
Message emailMessage = new MimeMessage(session);
// must be the email address of an administrator for the application. see docs
emailMessage.setFrom(new InternetAddress("jeffdonthemic@gmail.com","Jeff Douglas"));
emailMessage.addRecipient(Message.RecipientType.TO,
new InternetAddress(toAddress, toName));
emailMessage.setSubject("My Email Subject");
emailMessage.setText(messageBody);
Transport.send(emailMessage);
} catch (AddressException e) {
msg = e.toString();
} catch (MessagingException e) {
msg = e.toString();
} catch (UnsupportedEncodingException e) {
msg = e.toString();
}
return msg;
}
/**
* Sends a message to any XMPP-compatible chat messaging service (google talk).
* See http://code.google.com/appengine/docs/java/xmpp/overview.html
* for more detils
* @param recipient the jid of the jabber recipient of the notification
* @param msgBody the body of the message to be sent
*/
private void sendJabberNotification(String recipient, String msgBody) {
JID jid = new JID(recipient);
com.google.appengine.api.xmpp.Message msg = new MessageBuilder()
.withRecipientJids(jid)
.withBody(msgBody)
.build();
boolean messageSent = false;
XMPPService xmpp = XMPPServiceFactory.getXMPPService();
if (xmpp.getPresence(jid).isAvailable()) {
SendResponse status = xmpp.sendMessage(msg);
messageSent = (status.getStatusMap().get(jid) == SendResponse.Status.SUCCESS);
}
logger.info("Jabber notifiation sent: " + messageSent);
}
}