Tuesday, March 11, 2008

ical4j, JavaMail, Exchange and Outlook secret sauce

Last summer, I was tasked with sending out Outlook "meeting invites" for events in our product. This meant integrating with iCal4j. iCal4j itself has a truly bizarre API, but it wasn't bad to create a new invite, as there are so many examples out there. What really stumped me for a while was getting the headers right so that Outlook would pop up an "Accept/Decline" dialog for automatically adding the event to their calendar. I should have blogged about this last summer, as I've now forgotten what I finally did to make it work - I believe the real coup was getting the headers right. I remember finding lots of people with similar problems, so I wanted to get this code out there.

This code requires iCal4j and JavaMail 1.4. I believe the JavaMail 1.4-only ByteArrayDataSource is crucial for getting this to work. This code might not even work for you, I just wanted to get it out there as a starting point.

public MimeMessage createMimeMessage(Session _javaMailsession) throws Exception {
MimeMessage mimeMessage = new MimeMessage(_javaMailsession);
mimeMessage.addRecipients(Message.RecipientType.TO, getFromInternetAddresses());
Multipart multipart = new MimeMultipart();
MimeBodyPart iCalAttachment = new MimeBodyPart();
byte[] invite = createICalInvitation(getMeetingID(), getSubject(), getContent(), getMeetingStart(), getMeetingEnd(), getMeetingTimeZone());
iCalAttachment.setDataHandler(new DataHandler(new ByteArrayDataSource(new ByteArrayInputStream(invite), "text/calendar;method=REQUEST;charset=\"UTF-8\"")));
return mimeMessage;

private byte[] createICalInvitation(String _meetingID, String _subject, String _content, Date _start, Date _end, TimeZone _tz) throws Exception {
CompatibilityHints.setHintEnabled(CompatibilityHints.KEY_OUTLOOK_COMPATIBILITY, true);

VEvent vEvent = new VEvent();
vEvent.getProperties().add(new Uid(_meetingID));
vEvent.getProperties().add(new Summary(_subject));
vEvent.getProperties().add(new Description(_content));
vEvent.getProperties().add(new DtStart(new DateTime(_start)));
vEvent.getProperties().add(new DtEnd(new DateTime(_end)));

net.fortuna.ical4j.model.Calendar cal = new net.fortuna.ical4j.model.Calendar();
cal.getProperties().add(new ProdId("-//iloveoutlook//iCal4j 1.0//EN"));
TimeZoneRegistry registry = TimeZoneRegistryFactory.getInstance().createRegistry();
VTimeZone tz = registry.getTimeZone(_tz.getID()).getVTimeZone();

ByteArrayOutputStream bout = new ByteArrayOutputStream();
CalendarOutputter outputter = new CalendarOutputter();
outputter.output(cal, bout);
return bout.toByteArray();


Tal said...

Just wanted to thank you
Finally a good and simple example for ical4j and Outlook 2003

Anonymous said...

great example... thanks a lot.

Anonymous said...

Just wanted to say that the use of the ByteArray is unnecessary. The trick is the mime type headers. You don't need to use the byte array you can simply place calendar.toString() in the body of the email.

Anonymous said...

Did you work on rendezvous (send a day off) ?
I work with iCal4j and I always decline/accept window and I want save window.

Anonymous said...

Nice illustrative example, thank you.


Anonymous said...

Thanks for posting the sample! I've been struggling with this for a few hours now and your example is both functional and complete.

Alex said...

For work with Outlook I usually use other programs.But some days ago I found new program-how to open ost in outlook.And to my surprise program fixed all my old mails in seconds and for free.Moreover utility showed me how it open ost file in Outlook 2003 and recover your data from these encrypted *.ost files.

Moreshwar Thawkar said...
This comment has been removed by the author.
Moreshwar Thawkar said...

Hi All,

I have to create monthly recurring meeting for ms outlook and have written code using iCal4j lib. but unable to open ics file in outlook.. It is showing "Invalid ICS file".

Could you please help me with this issue.


Anonymous said...

thanks for posting it.