Recently I implemented a Confluence plugin for all kinds of events with all the common data you’d expect an event to have (title, description, start date, end date, location etc.). Event participants (i.e. Confluence users) would be able to register/unregister for and from events and event administrators could add/remove participants to and from events. It got most interesting, when I read in the specification that users were to be informed by Emails with ICalendar attachment when their Confluence accounts were connected to any activity within the event plugin.

Each of these activities would result in an ICalendar email, letting the Email tool of the user’s choice (e.g. Google Mail, Outlook) taking the role of their personal organizer by

  • Accepting invitations for events
  • Rejecting invitations for events
  • Registering events in the application’s calendar
  • Removing events from the application’s calendar
  • Registering changes of events

After reading the specification of these features I thought: “ok, so let’s find out how to send an Email with attachment from Confluence”. But while sending standard Emails from Confluence is a rather common task, adding attachments to these Emails apparently isn’t. I expected Confluence Email objects to expose a method addAttachment or anything similar. Since this is not the case, I had to find another Java-ish way to achieve this.

The following method expects a standard Confluence Email object as parameter and converts it into an email with attachment. For the sake of simplicity, I created a simple .txt file as attachment first:

public Email convertToMultipartMailObject(Email mail) {
  // Email text content. You might choose something more useful :)
  String messageBody = "This is the text content of the Email.";
  // Keep track if there were any errors within these method.
  boolean hasErrors = false;
  // Create the text part of the Email.
  MimeBodyPart messagePart = new MimeBodyPart();
  // Confluence Mails are Mime-Type text/html
  try {
    messagePart.setText(messageBody);
    messagePart.setHeader("Content-Type", "text/html");
  } catch (MessagingException e) {
    hasErrors = true;
    // I'm being lazy here, writing errors into the Email text string
    // you can do this more properly with logging and other mechanisms.
    messageBody = "ERROR: could not set body of multipart message!\n\n";
  }
  // This is going to be the text content of the Email .txt attachment.
  String attachment = "I'm gonna be the content of a .txt email attachment";
  // Create the Email attachment part.
  try {
    // "application/octet-stream" is the MIME-Type of choice for email attachments
    DataSource ds = new ByteArrayDataSource(attachment.getBytes("UTF-8"), "application/octet-stream");
    // Now we create the attachment part.
    MimeBodyPart attachmentPart = new MimeBodyPart();
    attachmentPart.setDataHandler(new DataHandler(ds));
    // File name of the attachment.
    attachmentPart.setFileName("mycrazyattachment.txt");
    // We need a multipart object that indicates that this mail has
    // several different parts.
    Multipart multipart = new MimeMultipart();
    // We add the text part.
    multipart.addBodyPart(messagePart);
    // And the attachment part!
    multipart.addBodyPart(attachmentPart);
    // Finally, we add the multipart object.
    mail.setMultipart(multipart);
  // Of course you would do some better error handling than the following:
  } catch (MessagingException e) {
    hasErrors = true;
    messageBody = "ERROR: could not set attachment of multipart message!\n\n";
  } catch (UnsupportedEncodingException e) {
    hasErrors = true;
    messageBody = "ERROR: could not set attachment of multipart message due to utf-8 encoding problems!\n\n";
  }
  // Now we set the subject of the Email.
  String subject = "This is the email's subject";
  mail.setSubject(subject);
  // You'd do better than this, but I'm lazy.
  if (hasErrors) {
    mail.setBody(messageBody);
  }
  // Yep, that's it. Now we have a Email object with attachment.
  // Let's return it so we can send it in the next step.
  return mail;
}

Yes, what we did in short is we took a Confluence Email object and converted into an Email object with a data attachment. Note that you cannot only use this pattern in your Confluence project, but in any Java code on a platform with Email support (e.g. Tomcat environments).

The above example created a simple .txt attachment with some text content. But as mentioned earlier, we want to create ICalendar attachment for your personal organizer. Fortunately, this is not a particularly difficult issue. In order to create a valid ICalendar attachment we must ensure that

I implemented a ICalendarAttachment class for instances wrapping ICalendar files. The class overrides the toString method, returning a ICalendar-formatted string, that can be embedded in a attachment file:

public class ICalendarAttachment {
// Constants for the ICalendar methods.
public static final String METHOD_CANCEL = "CANCEL";
public static final String METHOD_PUBLISH = "PUBLISH";
public static final String METHOD_REQUEST = "REQUEST";
// Constant for unique IDs. This can be made more unique, actually :).
private static final String UID_PREFIX = "scandio-confluence-events-";
// Apply this organizer, if no organizer is given.
private static final String DEFAULT_ORGANIZER = "donotreply@confluence.com";
// Common ICalendar fields.
private String method;
private Date beginDate;
private Date endDate;
private String location;
private int uid;
private String description;
private String subject;
private String organizer;
/**
 * Produces the string in proper format for an .ics attachment from the set instance
 * data.
 * @return VCalendar/.ics formatted string that can be used as attachment content.
 */
@Override
public String toString() {
  // Create the proper date format string required by the ICalendar spec.
  final SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
  final String stringBeginDate = formatter.format(beginDate);
  final String stringEndDate = formatter.format(endDate);
  // Encode the description => mark new lines.
  final String encodedDescription = description.replaceAll("\r\n", "\\\\n");
  // Use the default organizer if none is given.
  if (this.organizer == null) {
    this.organizer = DEFAULT_ORGANIZER;
  }
  // Content string as array.
  String[] contents = {
    "BEGIN:VCALENDAR",
    "METHOD:" + method,
    "BEGIN:VEVENT",
    "UID:" + UID_PREFIX + uid,
    "DTSTART:" + stringBeginDate,
    "DTEND:" + stringEndDate,
    "LOCATION:" + location,
    "DESCRIPTION:" + encodedDescription,
    "ORGANIZER:" + organizer,
    "SUMMARY:" + subject,
    "END:VEVENT",
    "END:VCALENDAR"
 };
 // Build a well-formatted string from the array.
 StringBuilder sb = new StringBuilder();
 for (String line : contents) {
   if (sb.length() > 0) {
     sb.append("\n");
   }
   sb.append(line);
 }
 // Return the well-formatter ICalendar string.
 return sb.toString();
}
// Getters and setters...
}

Now for example, when you receive an email with a ICalendar PUBLISH attachment, you will be prompted by the following screen in Outlook Express:

This article summarized how to create emails with attachments in Java and Confluence. It was shown how to create ICalendar attachments, that come to play very handy in your organizer tool of choice. I hope you enjoyed this article! Stay tuned @ Scandio TechBlog.