andrew Flower

Sending Calendar Events by Email in Java

I investigated this topic recently in order to generate Outlook events for people attending training in our company's Learning Management System. It was a bit tricky getting the right configuration so that Outlook recognised the calendar data correctly, so I thought I'd share it.

This shouldn't be limited to working for just Outlook calendars, but I haven't tested it for anything else and, as you know, the existence of a spec does not mean everyone is following it correctly.

This brief article will cover using the biweekly Java library to generate a Calendar event and sending that event in an email such that Outlook will recognise it.

This article is targeting the following products and versions:

Java 8+
Biweekly 0.6.4
JavaMail API 1.6.2

The accompanying source code is available here:

Creating a Calendar Event

The biweekly library makes it really easy to setup a Calendar, and output the iCal format as a string.

You can add the library using Gradle (or the equivalent Maven)

implementation 'net.sf.biweekly:biweekly:0.6.4'

Let's not waste any time, and look at a working example of creating an iCal event:

public static String generateICalData() {
    ICalendar ical = new ICalendar();
    ical.addProperty(new Method(Method.REQUEST));

        VEvent event = new VEvent();
            event.setSummary("Programming Hotdogs 101");
            event.setDescription("You having been invited to this amazing event! Let's program Hotdogs!");

            event.setDateStart(new Date());
            event.setDuration(new Duration.Builder()
                                      .hours(1)
                                      .build());

            event.setOrganizer(new Organizer("Amazing Organizer", "amazing.organizer@domain"));

            Attendee a = new Attendee("Not Amazing Attendee", "not.amazing.attendee@domain");
            a.setParticipationLevel(ParticipationLevel.REQUIRED);
            event.addAttendee(a);
        ical.addEvent(event);

    return Biweekly.write(ical).go();
}

The above code will generate an invite to a very simple 1 hour event starting at the current time and date, including one required attendee.

Line 3 is very important. It defines the Method being used to deliver Calendar information. The iCal format is not just a full calendar definition. It is more like a protocol for synchronizing calendars. For example, REQUEST is used to initially invite attendees as well as deliver updates about events. Attendees can use the REPLY Method to accept or decline. There are more methods, but for the purpose of this article we are only interested in REQUEST

The above code sample would generate the following string in iCal format:

BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Michael Angstadt//biweekly 0.6.4//EN
METHOD:REQUEST
BEGIN:VEVENT
UID:9664dfc1-d328-483f-ac6b-afa1d346bff2
DTSTAMP:20201023T051826Z
SUMMARY:Programming Hotdogs 101
DESCRIPTION:You having been invited to this amazing event! Let's program Ho
 tdogs!
DTSTART:20201023T051826Z
DURATION:PT1H
ORGANIZER;CN=Amazing Organizer:mailto:amazing.organizer@domain
ATTENDEE;ROLE=REQ-PARTICIPANT;CN=Not Amazing Attendee:mailto:not.amazing.at
 tendee@domain
END:VEVENT
END:VCALENDAR

Note the UID. This unique identifier can be used to send subsequent updates to events. newEvent.getUid() will give you the auto-generated ID, which can be saved for later and set with updatedEvent.setUid(..) to deliver an updated state of the event.

Sending with Java Mail

Let's now look at how to structure an Email with this event included. We will use MIME formats to indicate to Outlook that we are sending a Calendar.

The iCal string generated above must be sent with MIME type text/calendar. MIME allows for nesting multiple parts in an email message, but the easiest way to send via email is just to send the iCal as the message. Let's look at doing this with the JavaMail API.

implementation 'javax.mail:javax.mail-api:1.6.2'
implementation 'com.sun.mail:javax.mail:1.6.2'

Below we will send an event from the organizer to the attendee (It doesn't need to be the organizer sending the invite and could instead be some other third party)

// Create a mail session, specifying SMTP server
final Properties properties = System.getProperties();
properties.setProperty("mail.smtp.host", "your.smtp.domain");
final Session mailSession = Session.getDefaultInstance(properties);

final MimeMessage message = new MimeMessage(mailSession);

// Calendar as message content
final DataSource iCalData = new ByteArrayDataSource(generateICalData(), "text/calendar; charset=UTF-8");
message.setDataHandler(new DataHandler(iCalData));
message.setHeader("Content-Type", "text/calendar; charset=UTF-8; method=REQUEST");

// Send
final InternetAddress toAddress = new InternetAddress("not.amazing.attendee@domain", "Not Amazing Attendee");
final InternetAddress fromAddress = new InternetAddress("amazing.organizer@domain", "Amazing Organizer");
message.setRecipient(Message.RecipientType.TO, toAddress);
message.setSender(fromAddress);
Transport.send(message);

Most of this is standard email sending boilerplate. The interesting part is in lines 9-11 where we source the message content from the iCal string. Note that in line 11,

  1. the METHOD is specified again. This is necessary (at least for Outlook).
  2. The content type is set as text/calendar
  3. The Character set must be specified if you are using anything other than the default US-ASCII

The email text will be the same as the Calendar event description.

Adding Attachments

If you want to attach items to the email or event in Outlook, you can include them in a Multipart Email. MIME allows for encoding multiple 'files' of different types within a single email. The specification even allows for nesting different types of Multi-types (eg. multipart/mixed vs multipart/alternative).

To attach a file to the mail and event, all that is necessary is a multipart/mixed message comprising a calendar part and another part for the attachment. Below is an example with a plain text file.

...
// Calendar Part
final MimeBodyPart iCalPart = new MimeBodyPart();
final DataSource iCalData = new ByteArrayDataSource(generateICalData(), "text/calendar; charset=UTF-8");
iCalPart.setDataHandler(new DataHandler(iCalData));
iCalPart.setHeader("Content-Type", "text/calendar; charset=UTF-8; method=REQUEST");

// Text Part
final MimeBodyPart textAttachmentPart =  new MimeBodyPart();
textAttachmentPart.setContent("Some example contents ", "text/plain; charset=UTF-8");
textAttachmentPart.addHeader("Content-Disposition", "attachment; filename=fun.txt");

final MimeMessage message = new MimeMessage(mailSession);
final MimeMultipart mixedMultipart = new MimeMultipart("mixed");
mixedMultipart.addBodyPart(iCalPart);
mixedMultipart.addBodyPart(textAttachmentPart);
message.setContent(mixedMultipart);
...

This is similar to before in creating the Calendar data, but instead of setting the message to the Calendar content, we add it as a body part of the mixed multipart. In addition to that we create another body part in lines 9-11 which is a contrived text file attachment example.

Summary

In the above we learned the following:

  • How to generate a Calendar Event that can be recognized by Outlook when sent by mail
  • How to send generated Calendar data by email using JavaMail API
  • How to additionally include attachments with event invites

The accompanying source code is available here:

References