package com.wesabe.servlet.errors; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.net.InetAddress; import java.util.Date; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.Session; import javax.mail.Message.RecipientType; import javax.mail.internet.AddressException; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.google.common.collect.ImmutableList; /** * A base class for error reporting. * * @author coda */ public abstract class ErrorReporter { private static final Logger LOGGER = Logger.getLogger(ErrorReporter.class.getCanonicalName()); private final InternetAddress[] senders, recipients; private final String serviceName; /** * Create a new error reporter. * * @param from the email address from which the reports will be sent * @param to the email address(es) to which the reports will be sent * @param serviceName the name of the service being monitored for errors * @throws AddressException if {@code from} or {@code to} are invalid email addresses */ public ErrorReporter(String from, String to, String serviceName) throws AddressException { this.senders = InternetAddress.parse(from); this.recipients = InternetAddress.parse(to); this.serviceName = serviceName; } /** * Returns a list of the recipients of error reports. * * @return a list of the recipients of error reports */ public List<InternetAddress> getRecipients() { return ImmutableList.of(recipients); } /** * Returns a list of the senders of error reports. * * @return a list of the senders of error reports */ public List<InternetAddress> getSenders() { return ImmutableList.of(senders); } /** * Returns the service's name. * * @return the service's name */ public String getServiceName() { return serviceName; } /** * Returns the JavaMail session to be used to create email messages, or * {@code null} if the reporter handles delivery on its own. * * @return a JavaMail session or {@code null} */ public abstract Session getSession(); /** * Given an email message, a request, a response, and the exception thrown * during the processing of the request, notify the reporter's recipients of * the error. */ public abstract void deliver(Message message, HttpServletRequest request, HttpServletResponse response, Throwable e) throws IOException, MessagingException; /** * Report an exception raised during the processing of a request. */ public void report(HttpServletRequest request, HttpServletResponse response, Throwable exception) { try { deliver(buildEmail(request, exception), request, response, exception); } catch (final Exception e) { LOGGER.log(Level.SEVERE, "Unable to report error", e); LOGGER.log(Level.SEVERE, "Original error was:", exception); } } private Message buildEmail(HttpServletRequest request, Throwable e) throws MessagingException, IOException { final MimeMessage message = new MimeMessage(getSession()); message.addFrom(senders); message.addRecipients(RecipientType.TO, recipients); message.setSentDate(new Date()); message.setSubject(String.format("[ERROR] %s threw a %s", serviceName, e.getClass().getSimpleName())); message.setText(buildText(request, e), "UTF-8"); return message; } private String buildText(HttpServletRequest request, Throwable e) throws IOException { final ByteArrayOutputStream output = new ByteArrayOutputStream(); final PrintWriter email = new PrintWriter(output); email.print("A "); email.print(e.getClass().getCanonicalName()); email.print(" was thrown while responding to "); email.print(request.getRequestURL().toString()); email.print(" on "); email.print(InetAddress.getLocalHost().getHostName()); email.println(".\n"); email.print("This request from "); email.print(request.getRemoteAddr()); email.println(":\n"); email.print("\t"); email.println(request.toString().replaceAll("Authorization: .*", "Authorization: [REDACTED]").replace("\n", "\n\t")); email.println(); email.println("Produced this error:\n"); e.printStackTrace(email); email.flush(); return output.toString(); } }