Tuesday, October 16, 2007

Outlook calendar appointment email with Java 2.0

I have decided to finally update my blog. The only real traffic I get is from a post I wrote about 2 years ago. At the time I was working on a project to send an Outlook appointment using Java. I have had a lot of questions about how to do this without having to open the attachment. A few months ago, with the help from my colleague, Wayne Woodfield (check out his site www.jumpbid.com) we figured out how to mimic the actual Outlook message headers.

It is basically the same as before, with just a couple of additional headers. The start and end times are hard coded in the example below, so you are going to want to dynamically set those. We ended up logging into the mail server and viewing the message headers as text. This allowed us to find the proper format and the headers we were missing previously.

Here is the example:

import org.apache.commons.lang.StringUtils;

import java.util.Properties;
import java.util.Calendar;
import java.util.TimeZone;
import java.io.*;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.activation.DataHandler;
import javax.activation.DataSource;


public class OutlookMeeting
{
private String outgoingMailServer;
private String fromEmail;
private String toEmail;
private String subject;
private String rsvpTo;
private String organizer;
private SessionTime sessionTime;
private TimeZone timezone;
private String location;
private String message;

public static void main(String[] args) throws Exception {
OutlookMeeting meeting = new OutlookMeeting();
meeting.setOutgoingMailServer("mail.mailserver.com");
meeting.setFromEmail("test@test.com");
meeting.setToEmail("test@test.com");
meeting.setSubject("Outlook Meeting Request Using JavaMail");
meeting.setRsvpTo("test@test.com");
meeting.setOrganizer("test@test.com");
meeting.setTimeZone(TimeZone.getDefault());
meeting.setLocation("Test Location");
meeting.setMessage("This message was sent out with JavaMail");
meeting.send();
}

public void setOutgoingMailServer(String outgoingMailServer)
{
this.outgoingMailServer = outgoingMailServer;
}

public void setFromEmail(String fromEmail)
{
this.fromEmail = fromEmail;
}

public void setToEmail(String toEmail)
{
this.toEmail = toEmail;
}

public void setSubject(String subject)
{
this.subject = subject;
}

public void setRsvpTo(String rsvpTo)
{
this.rsvpTo = rsvpTo;
}

public void setOrganizer(String organizer)
{
this.organizer = organizer;
}

public void setSessionTime(SessionTime st)
{
this.sessionTime = st;
}

public void setTimeZone(TimeZone timezone)
{
this.timezone = timezone;
}

public void setLocation(String location)
{
this.location = location;
}

public void setMessage(String message)
{
this.message = message;
}

public void send() throws Exception
{
try
{
Properties prop = new Properties();
prop.put("mail.smtp.host", outgoingMailServer);

Session session = Session.getDefaultInstance(prop, null);

// Define message
MimeMessage message = new MimeMessage(session);
message.setFrom(new InternetAddress(fromEmail));
message.addRecipient(Message.RecipientType.TO, new InternetAddress(toEmail));
message.setSubject(subject);

String body = "BEGIN:VCALENDAR\n" +
"PRODID:-//Microsoft Corporation//Outlook 9.0 MIMEDIR//EN\n" +
"VERSION:2.0\n" +
"METHOD:REQUEST\n" +
"BEGIN:VEVENT\n" +
"ATTENDEE;ROLE=REQ-PARTICIPANT;RSVP=TRUE:MAILTO:" + StringUtils.defaultString(rsvpTo, fromEmail) + "\n" +
"ORGANIZER:MAILTO:" + StringUtils.defaultString(organizer, fromEmail) + "\n" +
"DTSTART:20070908T180000Z\n" +
"DTEND:20070908T190000Z\n" +
"LOCATION:" + location + "\n" +
"TRANSP:OPAQUE\n" +
"SEQUENCE:0\n" +
"UID:040000008200E00074C5B7101A82E00800000000002FF466CE3AC5010000000000000000100\n" +
" 000004377FE5C37984842BF9440448399EB02\n" +
"DTSTAMP:" + VCalUtil.getVCalDateTimeString(Calendar.getInstance(TimeZone.getTimeZone("GMT"))) + "\n" +
"CATEGORIES:Meeting\n" +
"DESCRIPTION:" + StringUtils.replace(this.message, "\n", "\\n") + "\n\n" +
"SUMMARY:" + subject + "\n" +
"PRIORITY:5\n" +
"CLASS:PUBLIC\n" +
"BEGIN:VALARM\n" +
"TRIGGER:-PT15M\n" +
"ACTION:DISPLAY\n" +
"DESCRIPTION:Reminder\n" +
"END:VALARM\n" +
"END:VEVENT\n" +
"END:VCALENDAR";

// Fill the message
DataHandler handler = new DataHandler(new OutlookMeetingDataSource(body));
message.setDataHandler(handler);

// send message
Transport.send(message);
}
catch (MessagingException me)
{
me.printStackTrace();
}
catch (Exception ex)
{
ex.printStackTrace();
}
}

class OutlookMeetingDataSource implements DataSource
{
private byte[] data;

public OutlookMeetingDataSource(String data) {
try
{
this.data = data.getBytes("UTF-8");
}
catch (UnsupportedEncodingException uex)
{
}
}

public InputStream getInputStream() throws IOException
{
return new ByteArrayInputStream(data);
}

public OutputStream getOutputStream() throws IOException
{
throw new IOException("Unsupported operation. We can only send Outlook Meeting invites with this handler, not receive them.");
}

public String getContentType()
{
return "text/calendar; method=REQUEST; charset=\"UTF-8\"";
}

public String getName()
{
return "Microsoft Outlook Calendar Item";
}
}
}

Hopefully this is a good update for people looking to send Outlook appointment emails. I will try and be better at responding to comments or questions.

2 comments:

Unknown said...

I am having a problem sending the calendar event email for ALL Day Event (where TRASNP value is TRANSPARENT). I am setting the following for start/end/transp:

String body = "BEGIN:VCALENDAR\n" +
"PRODID:-//Microsoft Corporation//Outlook 11.0 MIMEDIR//EN\n" +
"VERSION:2.0\n" +
"METHOD:REQUEST\n" +
"BEGIN:VEVENT\n" +
"ATTENDEE;ROLE=REQ-PARTICIPANT;RSVP=TRUE:MAILTO:" +
StringUtils.defaultString(rsvpTo, fromEmail) + "\n" +
"ORGANIZER:MAILTO:" + StringUtils.defaultString(organizer, fromEmail) + "\n" +
"DTSTART;VALUE=DATE:20071027\n" +
"DTEND;VALUE=DATE:20071028\n" +
"LOCATION:" + location + "\n" +
"TRANSP:TRANSPARENT\n" +
"SEQUENCE:0\n" +
"UID:040000008200E00074C5B7101A82E00800000000002FF466CE3AC5010000000000000000100\n" +
" 000004377FE5C37984842BF988848399EB02\n" +
"DTSTAMP:20071026T123500Z\n" +
"CATEGORIES:Meeting\n" +
"DESCRIPTION:" + StringUtils.replace(this.message, "\n", "\\n") + "\n\n" +
"SUMMARY:" + subject + "\n" +
"PRIORITY:5\n" +
"CLASS:PUBLIC\n" +
"BEGIN:VALARM\n" +
"TRIGGER:-PT15M\n" +
"ACTION:DISPLAY\n" +
"DESCRIPTION:Reminder\n" +
"END:VALARM\n" +
"END:VEVENT\n" +
"END:VCALENDAR";


It is showing up as spanning the two days from 12am on 27th to 12am 28. It is not an ALL day event and it is not marked as "Free" in the Show Time As outlook attribute.

Any help will be welcomed.

Unknown said...

Hi there!

I'm trying to do the same for month now but i can't seem to be able to make the accept and decline buttons appear on the message, only the vcal file appears in attachment. I was checking you're post and the following class is missing:

VCalUtil

Can you please post this class? Thanks in advance.

Regards

Pedro Pinto
pedro.spinto@sitel.pt