I needed to solve a couple of problems: 1) My simple JavaMail
code worked fine for standard SMTP servers like gmail,
but not for MS Exchange. 2) I was
already using the free BIRT web viewer to run reports. Now I needed to also send scheduled,
automated emails with PDF versions of the reports.
I created the class below which is set up as
a Spring scheduled task. The Spring
bean config looks like this:
<bean
id="DailyReportTask"
class="com.example.web.reports.ReportSender">
<property
name="recipients" value="someguy@example.com"/>
<property name="reportPath" value="Daily Report.rptdesign"/>
<property name="subject"
value="Daily Report"/>
<property name="body"
value="Today's Daily Report is attached below."/>
<property name="attachmentFileName" value="DailyReport.pdf"/>
<property name="reportParameters">
<map key-type="java.lang.String" value-type="java.lang.Object">
<entry key="Date">
<bean class="com.example.web.factory.AddToCurrentDateString">
<property name="datePattern" value="M/d/yyyy"/>
<property name="numberOfUnits" value="0"/>
</bean>
</entry>
</map>
</property>
</bean>
As you can see, all the settings that might
change from one report to the next are specified in Spring config. I can now set up as many different
reports as I want.
The “AddToCurrentDateString”
class is a simple class that does date arithmetic and spits out the result in
its toString() method. It lets me specify report parameters
dynamically, like “today” or “today – 7 days”.
Below is the meat of the code. The bit that allows it to work with MS
Exchange (at least in my case, I know Exchange implementations vary) is the DomainAuthenticator bit which concatenates the Windows
domain name with the username, and setting the JavaMail
property mail.smtp.auth to true.
As far as attaching the report is concerned,
there was no need to actually download the bytes of the report. I just needed to construct the full URL
that would generate a PDF from the BIRT web viewer, and MimeBodyPart.setDataHandler(URL)
took care of the rest.
package com.example.web.reports;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Map;
import java.util.Properties;
import javax.activation.DataHandler;
import javax.mail.Authenticator;
import javax.mail.BodyPart;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Part;
import javax.mail.PasswordAuthentication;
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.MimeMessage.RecipientType;
import javax.mail.internet.MimeMultipart;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.MailException;
import com.example.integration.ConfigService;
public class ReportSender {
private static final Logger LOG = Logger.getLogger(ReportSender.class);
private String reportPath;
// comma-separated list of recipient email addresses
private String recipients;
private String subject = "Automated Report";
private Object body = "";
private String attachmentFileName = "Report.pdf";
// map of BIRT report parameter names to their values - will be
included
// as parameters in the URL
private Map<String, Object> reportParameters;
@Autowired
private ConfigService
configService;
public void sendReport() {
try {
// load settings from config
String
host = configService.getConfig("smtp.host");
String
port = configService.getConfig("smtp.port", "25");
// for MS Exchange, specify the name of the Windows domain
String
domain = configService.getConfig("smtp.domain", "");
String
username = configService.getConfig("smtp.user");
String
password = configService.getConfig("smtp.password");
// for MS Exchange, this should be true
String
authenticate = configService.getConfig("smtp.authenticate", "true");
String
from = configService.getConfig("smtp.fromAddress");
// DomainAuthenticator will
concatenate the domain and username,
// separated by "\" - see below
DomainAuthenticator auth = new DomainAuthenticator(domain,
username, password);
// set up JavaMail properties
Properties
properties = new Properties();
properties.setProperty("mail.smtp.submitter", auth.getPasswordAuthentication().getUserName());
properties.setProperty("mail.smtp.auth", authenticate);
properties.setProperty("mail.smtp.host", host);
properties.setProperty("mail.smtp.port", port);
Session
session = Session.getInstance(properties,
auth);
// start constructing the message to send
MimeMessage message = new MimeMessage(session);
String[]
recipientList = StringUtils.split(recipients, ',');
for(String toAddr
: recipientList) {
message.addRecipient(RecipientType.TO, new InternetAddress(toAddr));
}
message.addFrom(new InternetAddress[] { new InternetAddress(from)
});
message.setSubject(subject);
// this will be a multipart message - plain text, plus a PDF
attachment
Multipart
multipart = new MimeMultipart();
BodyPart textPart = new MimeBodyPart();
textPart.setText(body != null ? body.toString() : "");
multipart.addBodyPart(textPart);
// build a URL that would result in a PDF file generated by the
BIRT web viewer
String
reportViewerUrl = configService.getConfig("reports.viewer.url");
StringBuilder fullUrl = new StringBuilder(reportViewerUrl)
.append("/preview?__format=pdf&__report=")
.append(URLEncoder.encode(reportPath, "UTF-8"));
// add report parameters, if any, to the URL
if(reportParameters != null && !reportParameters.isEmpty()) {
for(String key : reportParameters.keySet()) {
final Object val
= reportParameters.get(key);
fullUrl.append('&').append(URLEncoder.encode(key, "UTF-8"))
.append('=').append(URLEncoder.encode(val == null ? "" : val.toString(), "UTF-8"));
}
}
if(LOG.isDebugEnabled()) LOG.debug("Downloading
report URL: " + fullUrl);
// add the PDF attachment - no need to read it into memory, just
open
// a stream using the URL
BodyPart attachmentPart = new MimeBodyPart();
attachmentPart.setDataHandler(new DataHandler(new URL(fullUrl.toString())));
attachmentPart.setFileName(attachmentFileName);
attachmentPart.setDisposition(Part.ATTACHMENT);
attachmentPart.setHeader("Content-Type", "application/pdf");
multipart.addBodyPart(attachmentPart);
message.setContent(multipart);
Transport.send(message);
}
catch (MailException
e) {
LOG.error("MailException
while sending report email. Report path: " + reportPath, e);
}
catch (MessagingException
e) {
LOG.error("MessagingException
while sending report email. Report path: " + reportPath, e);
}
catch (UnsupportedEncodingException
e) {
LOG.error("UnsupportedEncodingException
while sending report email. Report path: " + reportPath, e);
}
catch (IOException
e) {
LOG.error("IOException
while sending report email. Report path: " + reportPath, e);
}
}
public void setReportPath(String reportPath) {
this.reportPath = reportPath;
}
public void setRecipients(String recipients) {
this.recipients = recipients;
}
public void setSubject(String subject) {
this.subject = subject;
}
public void setBody(Object body) {
this.body = body;
}
public void setAttachmentFileName(String attachmentFileName) {
this.attachmentFileName = attachmentFileName;
}
public void setReportParameters(Map<String,
Object> reportParameters) {
this.reportParameters = reportParameters;
}
private static class DomainAuthenticator extends Authenticator {
private String domain;
private String username;
private String password;
public DomainAuthenticator(String
domain, String username,
String
password) {
super();
this.domain = domain;
this.username = username;
this.password = password;
}
public PasswordAuthentication
getPasswordAuthentication() {
StringBuilder user = new StringBuilder();
if(!StringUtils.isBlank(this.domain)) user.append(this.domain).append('\\');
user.append(username);
return new PasswordAuthentication(user.toString(), this.password);
}
}
}
No comments:
Post a Comment